commit
						68673957ed
					
				
					 11 changed files with 4115 additions and 1377 deletions
				
			
		
							
								
								
									
										7
									
								
								QA.md
									
										
									
									
									
								
							
							
						
						
									
										7
									
								
								QA.md
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -20,10 +20,8 @@ The behaviors of the console are tested in [Console section](#consoles).
 | 
			
		|||
 | 
			
		||||
### Properties
 | 
			
		||||
 | 
			
		||||
- [ ] Configure custom hint character by `:set hintchars=012345678`
 | 
			
		||||
- [ ] Smooth scroll by `:set smoothscroll`
 | 
			
		||||
- [ ] Non-smooth scroll by `:set nosmoothscroll`
 | 
			
		||||
- [ ] Configure smooth scroll by settings `"smoothscroll": true`, `"smoothscroll": false`
 | 
			
		||||
- [ ] Toggle smooth scroll by `:set smoothscroll` and `:set nosmoothscroll`
 | 
			
		||||
- [ ] Configure smooth scroll by settings `"smoothscroll": true` and `"smoothscroll": false`
 | 
			
		||||
 | 
			
		||||
### Settings
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +43,6 @@ The behaviors of the console are tested in [Console section](#consoles).
 | 
			
		|||
##### Updating
 | 
			
		||||
 | 
			
		||||
- [ ] changes are updated on textarea blure when no errors
 | 
			
		||||
- [ ] changes are not updated on textarea blure when errors occurs
 | 
			
		||||
- [ ] keymap settings are applied to open tabs without reload
 | 
			
		||||
- [ ] search settings are applied to open tabs without reload
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ const lanthan = require('lanthan');
 | 
			
		|||
const path = require('path');
 | 
			
		||||
const assert = require('assert');
 | 
			
		||||
const eventually = require('./eventually');
 | 
			
		||||
const Console = require('./lib/Console');
 | 
			
		||||
 | 
			
		||||
const Key = lanthan.Key;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +115,6 @@ describe('follow properties test', () => {
 | 
			
		|||
      assert.equal(await hints[2].getStyle('display'), 'block');
 | 
			
		||||
      assert.equal(await hints[3].getStyle('display'), 'block');
 | 
			
		||||
      assert.equal(await hints[4].getStyle('display'), 'none');
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,4 +147,36 @@ describe('follow properties test', () => {
 | 
			
		|||
      assert.equal(tabs[1].active, false);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should show hints with hintchars by settings', async () => {
 | 
			
		||||
    let c = new Console(session);
 | 
			
		||||
 | 
			
		||||
    await body.sendKeys(':');
 | 
			
		||||
    await session.switchToFrame(0);
 | 
			
		||||
    await c.sendKeys('set hintchars=abc', Key.Enter);
 | 
			
		||||
    await session.switchToParentFrame();
 | 
			
		||||
 | 
			
		||||
    await body.sendKeys('f');
 | 
			
		||||
    await eventually(async() => {
 | 
			
		||||
      let hints = await session.findElementsByCSS('.vimvixen-hint');
 | 
			
		||||
      assert.equal(hints.length, 5);
 | 
			
		||||
 | 
			
		||||
      assert.equal(await hints[0].getText(), 'A');
 | 
			
		||||
      assert.equal(await hints[1].getText(), 'B');
 | 
			
		||||
      assert.equal(await hints[2].getText(), 'C');
 | 
			
		||||
      assert.equal(await hints[3].getText(), 'AA');
 | 
			
		||||
      assert.equal(await hints[4].getText(), 'AB');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    await body.sendKeys('a');
 | 
			
		||||
    await eventually(async() => {
 | 
			
		||||
      let hints = await session.findElementsByCSS('.vimvixen-hint');
 | 
			
		||||
 | 
			
		||||
      assert.equal(await hints[0].getStyle('display'), 'block');
 | 
			
		||||
      assert.equal(await hints[1].getStyle('display'), 'none');
 | 
			
		||||
      assert.equal(await hints[2].getStyle('display'), 'none');
 | 
			
		||||
      assert.equal(await hints[3].getStyle('display'), 'block');
 | 
			
		||||
      assert.equal(await hints[4].getStyle('display'), 'block');
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5371
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5371
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										12
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -24,16 +24,16 @@
 | 
			
		|||
    "@types/chai": "^4.1.7",
 | 
			
		||||
    "@types/mocha": "^5.2.6",
 | 
			
		||||
    "@types/prop-types": "^15.7.1",
 | 
			
		||||
    "@types/react": "^16.8.15",
 | 
			
		||||
    "@types/react": "^16.8.18",
 | 
			
		||||
    "@types/react-dom": "^16.8.4",
 | 
			
		||||
    "@types/react-redux": "^7.0.8",
 | 
			
		||||
    "@types/react-redux": "^7.0.9",
 | 
			
		||||
    "@types/redux-promise": "^0.5.28",
 | 
			
		||||
    "@types/sinon": "^7.0.11",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^1.9.0",
 | 
			
		||||
    "chai": "^4.2.0",
 | 
			
		||||
    "css-loader": "^2.1.1",
 | 
			
		||||
    "eslint": "^5.16.0",
 | 
			
		||||
    "eslint-plugin-react": "^7.12.4",
 | 
			
		||||
    "eslint-plugin-react": "^7.13.0",
 | 
			
		||||
    "html-webpack-plugin": "^3.2.0",
 | 
			
		||||
    "jszip": "^3.2.1",
 | 
			
		||||
    "karma": "^4.1.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +62,8 @@
 | 
			
		|||
    "tsyringe": "^3.2.0",
 | 
			
		||||
    "typescript": "^3.4.5",
 | 
			
		||||
    "web-ext-types": "^3.1.0",
 | 
			
		||||
    "webextensions-api-fake": "^0.7.4",
 | 
			
		||||
    "webpack": "^4.30.0",
 | 
			
		||||
    "webpack-cli": "^3.3.1"
 | 
			
		||||
    "webextensions-api-fake": "^0.8.0",
 | 
			
		||||
    "webpack": "^4.32.2",
 | 
			
		||||
    "webpack-cli": "^3.3.2"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,9 @@ export default class CompletionsUseCase {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  async queryOpen(name: string, keywords: string): Promise<CompletionGroup[]> {
 | 
			
		||||
    // TODO This logic contains view entities.  They should be defined on
 | 
			
		||||
    // content script
 | 
			
		||||
 | 
			
		||||
    let settings = await this.settingRepository.get();
 | 
			
		||||
    let groups: CompletionGroup[] = [];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +198,7 @@ export default class CompletionsUseCase {
 | 
			
		|||
      .map(pages => filters.filterByPathname(pages, COMPLETION_ITEM_LIMIT))
 | 
			
		||||
      .map(pages => filters.filterByOrigin(pages, COMPLETION_ITEM_LIMIT))[0]
 | 
			
		||||
      .sort((x: HistoryItem, y: HistoryItem): number => {
 | 
			
		||||
        return Number(x.visitCount) - Number(y.visitCount);
 | 
			
		||||
        return Number(y.visitCount) - Number(x.visitCount);
 | 
			
		||||
      })
 | 
			
		||||
      .slice(0, COMPLETION_ITEM_LIMIT);
 | 
			
		||||
    return histories.map(page => ({
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,16 +63,16 @@ export default class FollowSlaveUseCase {
 | 
			
		|||
 | 
			
		||||
    if (hint instanceof LinkHint) {
 | 
			
		||||
      let url = hint.getLink();
 | 
			
		||||
      // ignore taget='_blank'
 | 
			
		||||
      if (!newTab && hint.getLinkTarget() === '_blank') {
 | 
			
		||||
        hint.click();
 | 
			
		||||
        return;
 | 
			
		||||
      let openNewTab = newTab;
 | 
			
		||||
      // Open link by background script in order to prevent a popup block
 | 
			
		||||
      if (hint.getLinkTarget() === '_blank') {
 | 
			
		||||
        openNewTab = true;
 | 
			
		||||
      }
 | 
			
		||||
      // eslint-disable-next-line no-script-url
 | 
			
		||||
      if (!url || url === '#' || url.toLowerCase().startsWith('javascript:')) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      await this.tabsClient.openUrl(url, newTab, background);
 | 
			
		||||
      await this.tabsClient.openUrl(url, openNewTab, background);
 | 
			
		||||
    } else if (hint instanceof InputHint) {
 | 
			
		||||
      hint.activate();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ class SearchForm extends React.Component<Props> {
 | 
			
		|||
    </div>;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // eslint-disable-next-line max-statements
 | 
			
		||||
  bindValue(e: any) {
 | 
			
		||||
    let value = this.props.value.toJSON();
 | 
			
		||||
    let name = e.target.name;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +73,12 @@ class SearchForm extends React.Component<Props> {
 | 
			
		|||
      next.default = value.engines[index][0];
 | 
			
		||||
    } else if (name === 'add') {
 | 
			
		||||
      next.engines.push(['', '']);
 | 
			
		||||
    } else if (name === 'delete') {
 | 
			
		||||
    } else if (name === 'delete' && value.engines.length > 1) {
 | 
			
		||||
      next.engines.splice(index, 1);
 | 
			
		||||
      if (value.engines[index][0] === value.default) {
 | 
			
		||||
        let nextIndex = Math.min(index, next.engines.length - 1);
 | 
			
		||||
        next.default = next.engines[nextIndex][0];
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.props.onChange(FormSearch.valueOf(next));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,6 +175,7 @@ class SettingsComponent extends React.Component<Props> {
 | 
			
		|||
    if (from === 'form' && value === 'json') {
 | 
			
		||||
      this.props.dispatch(settingActions.switchToJson(
 | 
			
		||||
        this.props.form as FormSettings));
 | 
			
		||||
      this.save();
 | 
			
		||||
    } else if (from === 'json' && value === 'form') {
 | 
			
		||||
      let b = window.confirm(DO_YOU_WANT_TO_CONTINUE);
 | 
			
		||||
      if (!b) {
 | 
			
		||||
| 
						 | 
				
			
			@ -183,6 +184,7 @@ class SettingsComponent extends React.Component<Props> {
 | 
			
		|||
      }
 | 
			
		||||
      this.props.dispatch(
 | 
			
		||||
        settingActions.switchToForm(this.props.json as JSONSettings));
 | 
			
		||||
      this.save();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
/* eslint-disable max-len */
 | 
			
		||||
 | 
			
		||||
const fields = [
 | 
			
		||||
  [
 | 
			
		||||
    ['scroll.vertically?{"count":1}', 'Scroll down'],
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +21,8 @@ const fields = [
 | 
			
		|||
    ['tabs.close', 'Close a tab'],
 | 
			
		||||
    ['tabs.close.right', 'Close tabs to the right'],
 | 
			
		||||
    ['tabs.reopen', 'Reopen closed tab'],
 | 
			
		||||
    ['tabs.next?{"count":1}', 'Select next Tab'],
 | 
			
		||||
    ['tabs.prev?{"count":1}', 'Select prev Tab'],
 | 
			
		||||
    ['tabs.next', 'Select next Tab'],
 | 
			
		||||
    ['tabs.prev', 'Select prev Tab'],
 | 
			
		||||
    ['tabs.first', 'Select first tab'],
 | 
			
		||||
    ['tabs.last', 'Select last tab'],
 | 
			
		||||
    ['tabs.reload?{"cache":false}', 'Reload current tab'],
 | 
			
		||||
| 
						 | 
				
			
			@ -28,8 +30,8 @@ const fields = [
 | 
			
		|||
    ['tabs.pin.toggle', 'Toggle pinned state'],
 | 
			
		||||
    ['tabs.duplicate', 'Duplicate a tab'],
 | 
			
		||||
  ], [
 | 
			
		||||
    ['follow.start?{"newTab":false}', 'Follow a link'],
 | 
			
		||||
    ['follow.start?{"newTab":true}', 'Follow a link in new tab'],
 | 
			
		||||
    ['follow.start?{"newTab":false,"background":false}', 'Follow a link'],
 | 
			
		||||
    ['follow.start?{"newTab":true,"background":false}', 'Follow a link in new tab'],
 | 
			
		||||
    ['navigate.history.prev', 'Go back in histories'],
 | 
			
		||||
    ['navigate.history.next', 'Go forward in histories'],
 | 
			
		||||
    ['navigate.link.next', 'Open next link'],
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +39,7 @@ const fields = [
 | 
			
		|||
    ['navigate.parent', 'Go to parent directory'],
 | 
			
		||||
    ['navigate.root', 'Go to root directory'],
 | 
			
		||||
    ['page.source', 'Open page source'],
 | 
			
		||||
    ['page.home', 'Open start page to current tab'],
 | 
			
		||||
    ['page.home?{"newTab":false}', 'Open start page to current tab'],
 | 
			
		||||
    ['page.home?{"newTab":true}', 'Open start page in new tab'],
 | 
			
		||||
    ['focus.input', 'Focus input'],
 | 
			
		||||
  ], [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,17 +101,23 @@ export const blacklistValueOf = (o: any): string[] => {
 | 
			
		|||
 | 
			
		||||
export const valueOf = (o: any): Settings => {
 | 
			
		||||
  let settings = { ...DefaultSetting };
 | 
			
		||||
  if (Object.prototype.hasOwnProperty.call(o, 'keymaps')) {
 | 
			
		||||
    settings.keymaps = keymapsValueOf(o.keymaps);
 | 
			
		||||
  }
 | 
			
		||||
  if (Object.prototype.hasOwnProperty.call(o, 'search')) {
 | 
			
		||||
    settings.search = searchValueOf(o.search);
 | 
			
		||||
  }
 | 
			
		||||
  if (Object.prototype.hasOwnProperty.call(o, 'properties')) {
 | 
			
		||||
    settings.properties = propertiesValueOf(o.properties);
 | 
			
		||||
  }
 | 
			
		||||
  if (Object.prototype.hasOwnProperty.call(o, 'blacklist')) {
 | 
			
		||||
    settings.blacklist = blacklistValueOf(o.blacklist);
 | 
			
		||||
  for (let key of Object.keys(o)) {
 | 
			
		||||
    switch (key) {
 | 
			
		||||
    case 'keymaps':
 | 
			
		||||
      settings.keymaps = keymapsValueOf(o.keymaps);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'search':
 | 
			
		||||
      settings.search = searchValueOf(o.search);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'properties':
 | 
			
		||||
      settings.properties = propertiesValueOf(o.properties);
 | 
			
		||||
      break;
 | 
			
		||||
    case 'blacklist':
 | 
			
		||||
      settings.blacklist = blacklistValueOf(o.blacklist);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      throw new TypeError('unknown setting: ' + key);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return settings;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -186,5 +186,9 @@ describe('Settings', () => {
 | 
			
		|||
      expect(value.search.engines).to.be.an('object');
 | 
			
		||||
      expect(value.blacklist).to.be.empty;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('throws a TypeError with an unknown field', () => {
 | 
			
		||||
      expect(() => settings.valueOf({ name: 'alice' })).to.throw(TypeError)
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in a new issue