aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2019-10-09 11:50:52 +0000
committerGitHub <noreply@github.com>2019-10-09 11:50:52 +0000
commit18c72bf15c6bc7e4c88dd06d38ff861f29d66b1b (patch)
treef46720349e17c57db7bbfc55241b12c4410f2773
parent8eddcc1785a85bbe74be254d1055ebe5125dad10 (diff)
parent68f6211aac4177f3a70a40031dabbd1b61840071 (diff)
Merge pull request #655 from ueokande/partial-blacklist
Partial blacklist
-rw-r--r--e2e/blacklist.test.ts21
-rw-r--r--e2e/clipboard.test.ts16
-rw-r--r--e2e/command_addbookmark.test.ts2
-rw-r--r--e2e/command_bdelete.test.ts8
-rw-r--r--e2e/command_buffer.test.ts2
-rw-r--r--e2e/command_help.test.ts2
-rw-r--r--e2e/command_open.test.ts30
-rw-r--r--e2e/command_tabopen.test.ts20
-rw-r--r--e2e/command_winopen.test.ts19
-rw-r--r--e2e/completion.test.ts21
-rw-r--r--e2e/completion_buffers.test.ts25
-rw-r--r--e2e/completion_open.test.ts55
-rw-r--r--e2e/completion_set.test.ts13
-rw-r--r--e2e/console.test.ts4
-rw-r--r--e2e/follow.test.ts18
-rw-r--r--e2e/follow_properties.test.ts2
-rw-r--r--e2e/lib/SettingRepository.ts18
-rw-r--r--e2e/mark.test.ts2
-rw-r--r--e2e/navigate.test.ts14
-rw-r--r--e2e/options.test.ts14
-rw-r--r--e2e/options_form.test.ts16
-rw-r--r--e2e/partial_blacklist.test.ts58
-rw-r--r--e2e/settings.ts86
-rw-r--r--e2e/zoom.test.ts2
-rw-r--r--src/content/controllers/SettingController.ts3
-rw-r--r--src/content/di.ts2
-rw-r--r--src/content/repositories/AddressRepository.ts9
-rw-r--r--src/content/usecases/KeymapUseCase.ts15
-rw-r--r--src/settings/components/form/BlacklistForm.tsx52
-rw-r--r--src/settings/components/form/PartialBlacklistForm.scss28
-rw-r--r--src/settings/components/form/PartialBlacklistForm.tsx79
-rw-r--r--src/settings/components/index.tsx16
-rw-r--r--src/shared/settings/Blacklist.ts124
-rw-r--r--src/shared/settings/KeySequence.ts2
-rw-r--r--test/content/usecases/KeymapUseCase.test.ts133
-rw-r--r--test/settings/components/form/BlacklistForm.test.tsx25
-rw-r--r--test/shared/settings/Blacklist.test.ts183
37 files changed, 774 insertions, 365 deletions
diff --git a/e2e/blacklist.test.ts b/e2e/blacklist.test.ts
index 8bf1bd8..dec9d99 100644
--- a/e2e/blacklist.test.ts
+++ b/e2e/blacklist.test.ts
@@ -5,10 +5,12 @@ import TestServer from './lib/TestServer';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
+import Settings from "../src/shared/settings/Settings";
describe("blacklist test", () => {
let server = new TestServer().receiveContent('/*',
- `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html">`,
+ `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`,
);
let lanthan: Lanthan;
let webdriver: WebDriver;
@@ -24,17 +26,12 @@ describe("blacklist test", () => {
await server.start();
let url = server.url('/a').replace('http://', '');
- await browser.storage.local.set({
- settings: {
- source: 'json',
- json: `{
- "keymaps": {
- "j": { "type": "scroll.vertically", "count": 1 }
- },
- "blacklist": [ "${url}" ]
- }`,
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ keymaps: {
+ j: { type: "scroll.vertically", count: 1 },
},
- });
+ blacklist: [ url ],
+ }));
});
after(async() => {
@@ -46,7 +43,7 @@ describe("blacklist test", () => {
it('should disable add-on if the URL is in the blacklist', async () => {
let page = await Page.navigateTo(webdriver, server.url('/a'));
- await page.sendKeys('j')
+ await page.sendKeys('j');
let scrollY = await page.getScrollY();
assert.strictEqual(scrollY, 0);
diff --git a/e2e/clipboard.test.ts b/e2e/clipboard.test.ts
index 2b71ade..3f2b289 100644
--- a/e2e/clipboard.test.ts
+++ b/e2e/clipboard.test.ts
@@ -4,10 +4,11 @@ import * as path from 'path';
import TestServer from './lib/TestServer';
import eventually from './eventually';
import * as clipboard from './lib/clipboard';
-import settings from './settings';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver, Key } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
+import Settings from "../src/shared/settings/Settings";
describe("clipboard test", () => {
let server = new TestServer(12321).receiveContent('/happy', 'ok');
@@ -23,9 +24,14 @@ describe("clipboard test", () => {
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ search: {
+ default: "google",
+ engines: {
+ "google": "http://127.0.0.1:12321/google?q={}",
+ },
+ },
+ }));
await server.start();
});
@@ -42,7 +48,7 @@ describe("clipboard test", () => {
for (let tab of tabs.slice(1)) {
await browser.tabs.remove(tab.id);
}
- })
+ });
it('should copy current URL by y', async () => {
let page = await Page.navigateTo(webdriver, server.url('/#should_copy_url'));
diff --git a/e2e/command_addbookmark.test.ts b/e2e/command_addbookmark.test.ts
index bcc75ac..5344292 100644
--- a/e2e/command_addbookmark.test.ts
+++ b/e2e/command_addbookmark.test.ts
@@ -10,7 +10,7 @@ import Page from './lib/Page';
describe('addbookmark command test', () => {
let server = new TestServer().receiveContent('/happy', `
<!DOCTYPE html>
- <html lang="en"><head><title>how to be happy</title></head></html">`,
+ <html lang="en"><head><title>how to be happy</title></head></html>`,
);
let lanthan: Lanthan;
let webdriver: WebDriver;
diff --git a/e2e/command_bdelete.test.ts b/e2e/command_bdelete.test.ts
index c96034d..239074e 100644
--- a/e2e/command_bdelete.test.ts
+++ b/e2e/command_bdelete.test.ts
@@ -36,10 +36,10 @@ describe('bdelete/bdeletes command test', () => {
await browser.tabs.remove(tab.id);
}
await browser.tabs.update(tabs[0].id, { url: server.url('/site1'), pinned: true });
- await browser.tabs.create({ url: server.url('/site2'), pinned: true })
- await browser.tabs.create({ url: server.url('/site3'), pinned: true })
- await browser.tabs.create({ url: server.url('/site4'), })
- await browser.tabs.create({ url: server.url('/site5'), })
+ await browser.tabs.create({ url: server.url('/site2'), pinned: true });
+ await browser.tabs.create({ url: server.url('/site3'), pinned: true });
+ await browser.tabs.create({ url: server.url('/site4'), });
+ await browser.tabs.create({ url: server.url('/site5'), });
await eventually(async() => {
let handles = await webdriver.getAllWindowHandles();
diff --git a/e2e/command_buffer.test.ts b/e2e/command_buffer.test.ts
index 0036839..472502b 100644
--- a/e2e/command_buffer.test.ts
+++ b/e2e/command_buffer.test.ts
@@ -16,7 +16,7 @@ describe('buffer command test', () => {
<head>
<title>my_${req.path.slice(1)}</title>
</head>
- </html">`);
+ </html>`);
});
let lanthan: Lanthan;
let webdriver: WebDriver;
diff --git a/e2e/command_help.test.ts b/e2e/command_help.test.ts
index 9269d49..20035fd 100644
--- a/e2e/command_help.test.ts
+++ b/e2e/command_help.test.ts
@@ -34,7 +34,7 @@ describe("help command test", () => {
beforeEach(async() => {
page = await Page.navigateTo(webdriver, server.url());
- })
+ });
it('should open help page by help command ', async() => {
let console = await page.showConsole();
diff --git a/e2e/command_open.test.ts b/e2e/command_open.test.ts
index 6fb2645..ba9c51e 100644
--- a/e2e/command_open.test.ts
+++ b/e2e/command_open.test.ts
@@ -2,14 +2,15 @@ import * as path from 'path';
import * as assert from 'assert';
import TestServer from './lib/TestServer';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
+import Settings from "../src/shared/settings/Settings";
describe("open command test", () => {
- let server = new TestServer(12321)
+ let server = new TestServer()
.receiveContent('/google', 'google')
.receiveContent('/yahoo', 'yahoo');
let lanthan: Lanthan;
@@ -25,11 +26,16 @@ describe("open command test", () => {
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
-
await server.start();
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ search: {
+ default: "google",
+ engines: {
+ "google": server.url('/google?q={}'),
+ "yahoo": server.url('/yahoo?q={}'),
+ },
+ },
+ }));
});
after(async() => {
@@ -42,7 +48,7 @@ describe("open command test", () => {
beforeEach(async() => {
await webdriver.switchTo().defaultContent();
page = await Page.navigateTo(webdriver, server.url());
- })
+ });
it('should open default search for keywords by open command ', async() => {
let console = await page.showConsole();
@@ -60,7 +66,7 @@ describe("open command test", () => {
await console.execCommand('open yahoo an apple');
await eventually(async() => {
- let tabs = await browser.tabs.query({ active: true })
+ let tabs = await browser.tabs.query({ active: true });
let url = new URL(tabs[0].url);
assert.strictEqual(url.href, server.url('/yahoo?q=an%20apple'))
});
@@ -71,7 +77,7 @@ describe("open command test", () => {
await console.execCommand('open');
await eventually(async() => {
- let tabs = await browser.tabs.query({ active: true })
+ let tabs = await browser.tabs.query({ active: true });
let url = new URL(tabs[0].url);
assert.strictEqual(url.href, server.url('/google?q='))
});
@@ -82,7 +88,7 @@ describe("open command test", () => {
await console.execCommand('open yahoo');
await eventually(async() => {
- let tabs = await browser.tabs.query({ active: true })
+ let tabs = await browser.tabs.query({ active: true });
let url = new URL(tabs[0].url);
assert.strictEqual(url.href, server.url('/yahoo?q='))
});
@@ -93,7 +99,7 @@ describe("open command test", () => {
await console.execCommand('open example.com');
await eventually(async() => {
- let tabs = await browser.tabs.query({ active: true })
+ let tabs = await browser.tabs.query({ active: true });
let url = new URL(tabs[0].url);
assert.strictEqual(url.href, 'http://example.com/')
});
@@ -104,7 +110,7 @@ describe("open command test", () => {
await console.execCommand('open https://example.com/');
await eventually(async() => {
- let tabs = await browser.tabs.query({ active: true })
+ let tabs = await browser.tabs.query({ active: true });
let url = new URL(tabs[0].url);
assert.strictEqual(url.href, 'https://example.com/')
});
diff --git a/e2e/command_tabopen.test.ts b/e2e/command_tabopen.test.ts
index 9d3da9a..b5533e6 100644
--- a/e2e/command_tabopen.test.ts
+++ b/e2e/command_tabopen.test.ts
@@ -2,14 +2,15 @@ import * as path from 'path';
import * as assert from 'assert';
import TestServer from './lib/TestServer';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
+import Settings from "../src/shared/settings/Settings";
describe("tabopen command test", () => {
- let server = new TestServer(12321)
+ let server = new TestServer()
.receiveContent('/google', 'google')
.receiveContent('/yahoo', 'yahoo');
let lanthan: Lanthan;
@@ -25,11 +26,16 @@ describe("tabopen command test", () => {
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
-
await server.start();
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ search: {
+ default: "google",
+ engines: {
+ "google": server.url('/google?q={}'),
+ "yahoo": server.url('/yahoo?q={}'),
+ },
+ },
+ }));
});
after(async() => {
@@ -46,7 +52,7 @@ describe("tabopen command test", () => {
}
page = await Page.navigateTo(webdriver, server.url());
- })
+ });
it('should open default search for keywords by tabopen command ', async() => {
let console = await page.showConsole();
diff --git a/e2e/command_winopen.test.ts b/e2e/command_winopen.test.ts
index 95a0b6a..fb1348d 100644
--- a/e2e/command_winopen.test.ts
+++ b/e2e/command_winopen.test.ts
@@ -2,14 +2,15 @@ import * as path from 'path';
import * as assert from 'assert';
import TestServer from './lib/TestServer';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
+import Settings from "../src/shared/settings/Settings";
describe("winopen command test", () => {
- let server = new TestServer(12321)
+ let server = new TestServer()
.receiveContent('/google', 'google')
.receiveContent('/yahoo', 'yahoo');
let lanthan: Lanthan;
@@ -24,11 +25,17 @@ describe("winopen command test", () => {
.build();
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
await server.start();
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ search: {
+ default: "google",
+ engines: {
+ "google": server.url('/google?q={}'),
+ "yahoo": server.url('/yahoo?q={}'),
+ },
+ },
+ }));
});
after(async() => {
@@ -45,7 +52,7 @@ describe("winopen command test", () => {
}
page = await Page.navigateTo(webdriver, server.url());
- })
+ });
it('should open default search for keywords by winopen command ', async() => {
let console = await page.showConsole();
diff --git a/e2e/completion.test.ts b/e2e/completion.test.ts
index afa4432..e98e1c2 100644
--- a/e2e/completion.test.ts
+++ b/e2e/completion.test.ts
@@ -2,7 +2,6 @@ import * as path from 'path';
import * as assert from 'assert';
import eventually from './eventually';
-import settings from './settings';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver, Key } from 'selenium-webdriver';
import Page from './lib/Page';
@@ -10,7 +9,6 @@ import Page from './lib/Page';
describe("general completion test", () => {
let lanthan: Lanthan;
let webdriver: WebDriver;
- let browser: any;
let page: Page;
before(async() => {
@@ -19,11 +17,6 @@ describe("general completion test", () => {
.spyAddon(path.join(__dirname, '..'))
.build();
webdriver = lanthan.getWebDriver();
- browser = lanthan.getWebExtBrowser();
-
- await browser.storage.local.set({
- settings,
- });
});
after(async() => {
@@ -42,8 +35,8 @@ describe("general completion test", () => {
let items = await console.getCompletions();
assert.strictEqual(items.length, 11);
assert.deepStrictEqual(items[0], { type: 'title', text: 'Console Command' });
- assert.ok(items[1].text.startsWith('set'))
- assert.ok(items[2].text.startsWith('open'))
+ assert.ok(items[1].text.startsWith('set'));
+ assert.ok(items[2].text.startsWith('open'));
assert.ok(items[3].text.startsWith('tabopen'))
});
@@ -54,8 +47,8 @@ describe("general completion test", () => {
let items = await console.getCompletions();
assert.strictEqual(items.length, 4);
assert.deepStrictEqual(items[0], { type: 'title', text: 'Console Command' });
- assert.ok(items[1].text.startsWith('buffer'))
- assert.ok(items[2].text.startsWith('bdelete'))
+ assert.ok(items[1].text.startsWith('buffer'));
+ assert.ok(items[2].text.startsWith('bdelete'));
assert.ok(items[3].text.startsWith('bdeletes'))
});
@@ -74,14 +67,14 @@ describe("general completion test", () => {
await console.sendKeys(Key.TAB);
await eventually(async() => {
let items = await console.getCompletions();
- assert.ok(items[1].highlight)
+ assert.ok(items[1].highlight);
assert.strictEqual(await console.currentValue(), 'buffer');
});
await console.sendKeys(Key.TAB, Key.TAB);
await eventually(async() => {
let items = await console.getCompletions();
- assert.ok(items[3].highlight)
+ assert.ok(items[3].highlight);
assert.strictEqual(await console.currentValue(), 'bdeletes');
});
@@ -93,7 +86,7 @@ describe("general completion test", () => {
await console.sendKeys(Key.SHIFT, Key.TAB);
await eventually(async() => {
let items = await console.getCompletions();
- assert.ok(items[3].highlight)
+ assert.ok(items[3].highlight);
assert.strictEqual(await console.currentValue(), 'bdeletes');
});
});
diff --git a/e2e/completion_buffers.test.ts b/e2e/completion_buffers.test.ts
index b2d4201..b6e7de0 100644
--- a/e2e/completion_buffers.test.ts
+++ b/e2e/completion_buffers.test.ts
@@ -3,7 +3,6 @@ import * as path from 'path';
import { Request, Response } from 'express'
import TestServer from './lib/TestServer';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
@@ -17,7 +16,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
<head>
<title>title_${req.path.slice(1)}</title>
</head>
- </html">`);
+ </html>`);
});
let lanthan: Lanthan;
let webdriver: WebDriver;
@@ -32,10 +31,6 @@ describe("completion on buffer/bdelete/bdeletes", () => {
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
-
await server.start();
});
@@ -53,7 +48,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
}
await browser.tabs.update(tabs[0].id, { url: server.url('/site1'), pinned: true });
- await browser.tabs.create({ url:server.url('/site2'), pinned: true })
+ await browser.tabs.create({ url:server.url('/site2'), pinned: true });
for (let i = 3; i <= 5; ++i) {
await browser.tabs.create({ url: server.url('/site' + i) });
}
@@ -84,7 +79,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[3].text.includes('%'));
assert.ok(items[5].text.includes('#'));
});
- })
+ });
it('should filter items with URLs by keywords on "buffer" command', async() => {
let console = await page.showConsole();
@@ -97,7 +92,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[1].text.includes('title_site2'));
assert.ok(items[1].text.includes(server.url('/site2')));
});
- })
+ });
it('should filter items with titles by keywords on "buffer" command', async() => {
let console = await page.showConsole();
@@ -108,7 +103,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' });
assert.ok(items[1].text.startsWith('2:'));
});
- })
+ });
it('should show one item by number on "buffer" command', async() => {
let console = await page.showConsole();
@@ -120,7 +115,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.deepStrictEqual(items[0], { type: 'title', text: 'Buffers' });
assert.ok(items[1].text.startsWith('2:'));
});
- })
+ });
it('should show unpinned tabs "bdelete" command', async() => {
let console = await page.showConsole();
@@ -133,7 +128,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[2].text.includes('site4'));
assert.ok(items[3].text.includes('site5'));
});
- })
+ });
it('should show unpinned tabs "bdeletes" command', async() => {
let console = await page.showConsole();
@@ -146,7 +141,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[2].text.includes('site4'));
assert.ok(items[3].text.includes('site5'));
});
- })
+ });
it('should show both pinned and unpinned tabs "bdelete!" command', async() => {
let console = await page.showConsole();
@@ -161,7 +156,7 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[4].text.includes('site4'));
assert.ok(items[5].text.includes('site5'));
});
- })
+ });
it('should show both pinned and unpinned tabs "bdeletes!" command', async() => {
let console = await page.showConsole();
@@ -176,5 +171,5 @@ describe("completion on buffer/bdelete/bdeletes", () => {
assert.ok(items[4].text.includes('site4'));
assert.ok(items[5].text.includes('site5'));
});
- })
+ });
});
diff --git a/e2e/completion_open.test.ts b/e2e/completion_open.test.ts
index c957e2e..5f8bd11 100644
--- a/e2e/completion_open.test.ts
+++ b/e2e/completion_open.test.ts
@@ -1,12 +1,13 @@
import * as path from 'path';
import * as assert from 'assert';
+import Settings from "../src/shared/settings/Settings";
import TestServer from './lib/TestServer';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
import Page from './lib/Page';
+import SettingRepository from "./lib/SettingRepository";
describe("completion on open/tabopen/winopen commands", () => {
let server = new TestServer().receiveContent('/*', 'ok');
@@ -25,10 +26,6 @@ describe("completion on open/tabopen/winopen commands", () => {
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- await browser.storage.local.set({
- settings,
- });
-
// Add item into hitories
await webdriver.navigate().to(('https://i-beam.org/404'));
});
@@ -65,7 +62,7 @@ describe("completion on open/tabopen/winopen commands", () => {
let items = completions.filter(x => x.type === 'item').map(x => x.text);
assert.ok(items.every(x => x.includes('https://')));
});
- })
+ });
it('should filter items with titles by keywords on "open" command', async() => {
let console = await page.showConsole();
@@ -76,7 +73,7 @@ describe("completion on open/tabopen/winopen commands", () => {
let items = completions.filter(x => x.type === 'item').map(x => x.text);
assert.ok(items.every(x => x.toLowerCase().includes('getting')));
});
- })
+ });
it('should filter items with titles by keywords on "tabopen" command', async() => {
let console = await page.showConsole();
@@ -87,7 +84,7 @@ describe("completion on open/tabopen/winopen commands", () => {
let items = completions.filter(x => x.type === 'item').map(x => x.text);
assert.ok(items.every(x => x.includes('https://')));
});
- })
+ });
it('should filter items with titles by keywords on "winopen" command', async() => {
let console = await page.showConsole();
@@ -98,7 +95,7 @@ describe("completion on open/tabopen/winopen commands", () => {
let items = completions.filter(x => x.type === 'item').map(x => x.text);
assert.ok(items.every(x => x.includes('https://')));
});
- })
+ });
it('should display only specified items in "complete" property by set command', async() => {
let console = await page.showConsole();
@@ -127,24 +124,12 @@ describe("completion on open/tabopen/winopen commands", () => {
let titles = completions.filter(x => x.type === 'title').map(x => x.text);
assert.deepStrictEqual(titles, ['Bookmarks', 'Search Engines', 'Search Engines'])
});
- })
+ });
it('should display only specified items in "complete" property by setting', async() => {
- await browser.storage.local.set({ settings: {
- source: 'json',
- json: `{
- "keymaps": {
- ":": { "type": "command.show" }
- },
- "search": {
- "default": "google",
- "engines": { "google": "https://google.com/search?q={}" }
- },
- "properties": {
- "complete": "sbh"
- }
- }`,
- }});
+ new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ properties: { complete: "sbh" },
+ }));
let console = await page.showConsole();
await console.inputKeys('open ');
@@ -158,21 +143,9 @@ describe("completion on open/tabopen/winopen commands", () => {
await console.close();
await (webdriver.switchTo() as any).parentFrame();
- await browser.storage.local.set({ settings: {
- source: 'json',
- json: `{
- "keymaps": {
- ":": { "type": "command.show" }
- },
- "search": {
- "default": "google",
- "engines": { "google": "https://google.com/search?q={}" }
- },
- "properties": {
- "complete": "bss"
- }
- }`,
- }});
+ new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ properties: { complete: "bss" },
+ }));
console = await page.showConsole();
await console.inputKeys('open ');
@@ -182,5 +155,5 @@ describe("completion on open/tabopen/winopen commands", () => {
let titles = completions.filter(x => x.type === 'title').map(x => x.text);
assert.deepStrictEqual(titles, ['Bookmarks', 'Search Engines', 'Search Engines'])
});
- })
+ });
});
diff --git a/e2e/completion_set.test.ts b/e2e/completion_set.test.ts
index 2a14b2c..facf991 100644
--- a/e2e/completion_set.test.ts
+++ b/e2e/completion_set.test.ts
@@ -1,7 +1,6 @@
import * as path from 'path';
import * as assert from 'assert';
-import settings from './settings';
import eventually from './eventually';
import { Builder, Lanthan } from 'lanthan';
import { WebDriver } from 'selenium-webdriver';
@@ -10,7 +9,6 @@ import Page from './lib/Page';
describe("completion on set commands", () => {
let lanthan: Lanthan;
let webdriver: WebDriver;
- let browser: any;
let page: Page;
before(async() => {
@@ -19,11 +17,6 @@ describe("completion on set commands", () => {
.spyAddon(path.join(__dirname, '..'))
.build();
webdriver = lanthan.getWebDriver();
- browser = lanthan.getWebExtBrowser();
-
- await browser.storage.local.set({
- settings,
- });
});
after(async() => {
@@ -44,9 +37,9 @@ describe("completion on set commands", () => {
let items = await console.getCompletions();
assert.strictEqual(items.length, 5);
assert.deepStrictEqual(items[0], { type: 'title', text: 'Properties' });
- assert.ok(items[1].text.startsWith('hintchars'))
- assert.ok(items[2].text.startsWith('smoothscroll'))
- assert.ok(items[3].text.startsWith('nosmoothscroll'))
+ assert.ok(items[1].text.startsWith('hintchars'));
+ assert.ok(items[2].text.startsWith('smoothscroll'));
+ assert.ok(items[3].text.startsWith('nosmoothscroll'));
assert.ok(items[4].text.startsWith('complete'))
});
});
diff --git a/e2e/console.test.ts b/e2e/console.test.ts
index 583580a..faaf695 100644
--- a/e2e/console.test.ts
+++ b/e2e/console.test.ts
@@ -7,8 +7,8 @@ import { WebDriver, Key } from 'selenium-webdriver';
import Page from './lib/Page';
describe("console test", () => {
- let server = new TestServer().receiveContent('/',
- `<!DOCTYPE html><html lang="en"><head><title>Hello, world!</title></head></html">`,
+ let server = new TestServer().receiveContent('/',
+ `<!DOCTYPE html><html lang="en"><head><title>Hello, world!</title></head></html>`,
);
let lanthan: Lanthan;
let webdriver: WebDriver;
diff --git a/e2e/follow.test.ts b/e2e/follow.test.ts
index fd741ef..ce3f565 100644
--- a/e2e/follow.test.ts
+++ b/e2e/follow.test.ts
@@ -14,13 +14,13 @@ const newApp = () => {
<!DOCTYPE html>
<html lang="en"><body>
<a href="hello">hello</a>
- </body></html">`);
+ </body></html>`);
server.receiveContent('/follow-input', `
<!DOCTYPE html>
<html lang="en"><body>
<input>
- </body></html">`);
+ </body></html>`);
server.receiveContent('/area', `
<!DOCTYPE html>
@@ -34,8 +34,8 @@ const newApp = () => {
<area shape="rect" coords="64,64,64,64" href="/">
<area shape="rect" coords="128,128,64,64" href="/">
</map>
- </body></html">`);
-
+ </body></html>`);
+
/*
* test case: link2 is out of the viewport
* +-----------------+
@@ -52,7 +52,7 @@ const newApp = () => {
<div><a href="link1">link1</a></div>
<div style="min-height:3000px"></div>
<div><a href="link2">link2</a></div>
- </body></html">`);
+ </body></html>`);
/*
* test case 2: link2 and link3 are out of window of the frame
@@ -69,14 +69,14 @@ const newApp = () => {
<!DOCTYPE html>
<html lang="en"><body>
<iframe height="5000" src='/test2-frame'>
- </body></html">`);
+ </body></html>`);
server.receiveContent('/test2-frame', `
<!DOCTYPE html>
<html lang="en"><body>
<div><a href="link1">link1</a></div>
<div style="min-height:3000px"></div>
<div><a href="link2">link2</a></div>
- </body></html">`);
+ </body></html>`);
/* test case 3: link2 is out of window of the frame
* +-----------------+
@@ -92,14 +92,14 @@ const newApp = () => {
<!DOCTYPE html>
<html lang="en"><body>
<iframe src='/test3-frame'>
- </body></html">`);
+ </body></html>`);
server.receiveContent('/test3-frame', `
<!DOCTYPE html>
<html lang="en"><body>
<div><a href="link1">link1</a></div>
<div style="min-height:3000px"></div>
<div><a href="link2">link2</a></div>
- </body></html">`);
+ </body></html>`);
return server;
};
diff --git a/e2e/follow_properties.test.ts b/e2e/follow_properties.test.ts
index 75a1d77..eaa38e2 100644
--- a/e2e/follow_properties.test.ts
+++ b/e2e/follow_properties.test.ts
@@ -16,7 +16,7 @@ describe('follow properties test', () => {
<a href="/">link3</a>
<a href="/">link4</a>
<a href="/">link5</a>
- </body></html">`);
+ </body></html>`);
let lanthan: Lanthan;
let webdriver: WebDriver;
diff --git a/e2e/lib/SettingRepository.ts b/e2e/lib/SettingRepository.ts
new file mode 100644
index 0000000..9d5d5aa
--- /dev/null
+++ b/e2e/lib/SettingRepository.ts
@@ -0,0 +1,18 @@
+import { JSONTextSettings, SettingSource } from '../../src/shared/SettingData';
+import Settings from '../../src/shared/settings/Settings';
+
+export default class SettingRepository {
+ constructor(
+ private readonly browser: any,
+ ) {
+ }
+
+ async saveJSON(settings: Settings): Promise<void> {
+ await this.browser.storage.local.set({
+ settings: {
+ source: SettingSource.JSON,
+ json: JSONTextSettings.fromSettings(settings).toJSONText(),
+ }
+ });
+ }
+}
diff --git a/e2e/mark.test.ts b/e2e/mark.test.ts
index 57a8fa6..f9f372b 100644
--- a/e2e/mark.test.ts
+++ b/e2e/mark.test.ts
@@ -9,7 +9,7 @@ import Page from './lib/Page';
describe("mark test", () => {
let server = new TestServer().receiveContent('/',
- `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html">`,
+ `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`,
);
let lanthan: Lanthan;
let webdriver: WebDriver;
diff --git a/e2e/navigate.test.ts b/e2e/navigate.test.ts
index 8ee209b..15c5a31 100644
--- a/e2e/navigate.test.ts
+++ b/e2e/navigate.test.ts
@@ -16,7 +16,7 @@ const newApp = () => {
<html lang="en">
<a href="/pagenation-a/${Number(req.params.page) - 1}">prev</a>
<a href="/pagenation-a/${Number(req.params.page) + 1}">next</a>
- </html">`);
+ </html>`);
});
server.handle('/pagenation-link/:page', (req, res) => {
@@ -27,7 +27,7 @@ const newApp = () => {
<link rel="prev" href="/pagenation-link/${Number(req.params.page) - 1}"></link>
<link rel="next" href="/pagenation-link/${Number(req.params.page) + 1}"></link>
</head>
- </html">`);
+ </html>`);
});
server.receiveContent('/reload', `
<!DOCTYPE html>
@@ -36,7 +36,7 @@ const newApp = () => {
<script>window.location.hash = Date.now()</script>
</head>
<body style="width:10000px; height:10000px"></body>
- </html">`);
+ </html>`);
server.receiveContent('/*', `ok`);
@@ -75,7 +75,7 @@ describe("navigate test", () => {
for (let tab of tabs.slice(1)) {
await browser.tabs.remove(tab.id);
}
- })
+ });
it('should go to parent path without hash by gu', async () => {
let page = await Page.navigateTo(webdriver, server.url('/a/b/c'));
@@ -95,7 +95,7 @@ describe("navigate test", () => {
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
let url = new URL(tab.url);
- assert.strictEqual(url.hash, '')
+ assert.strictEqual(url.hash, '');
assert.strictEqual(url.pathname, `/a/b/c`)
});
});
@@ -213,7 +213,7 @@ describe("navigate test", () => {
await page.sendKeys('r');
- let after
+ let after;
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
after = Number(new URL(tab.url).hash.split('#')[1]);
@@ -239,7 +239,7 @@ describe("navigate test", () => {
await page.sendKeys(Key.SHIFT, 'R');
- let after
+ let after;
await eventually(async() => {
let tab = (await browser.tabs.query({}))[0];
after = Number(new URL(tab.url).hash.split('#')[1]);
diff --git a/e2e/options.test.ts b/e2e/options.test.ts
index 8d5023f..f418dc3 100644
--- a/e2e/options.test.ts
+++ b/e2e/options.test.ts
@@ -10,7 +10,7 @@ import OptionPage from './lib/OptionPage';
describe("options page", () => {
let server = new TestServer().receiveContent('/',
- `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html">`,
+ `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`,
);
let lanthan: Lanthan;
let webdriver: WebDriver;
@@ -39,22 +39,22 @@ describe("options page", () => {
for (let tab of tabs.slice(1)) {
await browser.tabs.remove(tab.id);
}
- })
+ });
it('saves current config on blur', async () => {
let page = await OptionPage.open(lanthan);
let jsonPage = await page.asJSONOptionPage();
- await jsonPage.updateSettings(`{ "blacklist": [ "https://example.com" ] }`)
+ await jsonPage.updateSettings(`{ "blacklist": [ "https://example.com" ] }`);
let { settings } = await browser.storage.local.get('settings');
- assert.strictEqual(settings.source, 'json')
- assert.strictEqual(settings.json, '{ "blacklist": [ "https://example.com" ] } ')
+ assert.strictEqual(settings.source, 'json');
+ assert.strictEqual(settings.json, '{ "blacklist": [ "https://example.com" ] } ');
await jsonPage.updateSettings(`invalid json`);
settings = (await browser.storage.local.get('settings')).settings;
- assert.strictEqual(settings.source, 'json')
- assert.strictEqual(settings.json, '{ "blacklist": [ "https://example.com" ] } ')
+ assert.strictEqual(settings.source, 'json');
+ assert.strictEqual(settings.json, '{ "blacklist": [ "https://example.com" ] } ');
let message = await jsonPage.getErrorMessage();
assert.ok(message.startsWith('SyntaxError:'))
diff --git a/e2e/options_form.test.ts b/e2e/options_form.test.ts
index af53791..6023f9c 100644
--- a/e2e/options_form.test.ts
+++ b/e2e/options_form.test.ts
@@ -19,13 +19,13 @@ describe("options form page", () => {
for (let tab of tabs.slice(1)) {
await browser.tabs.remove(tab.id);
}
- })
+ });
afterEach(async() => {
if (lanthan) {
await lanthan.quit();
}
- })
+ });
it('switch to form settings', async () => {
let page = await OptionPage.open(lanthan);
@@ -33,7 +33,7 @@ describe("options form page", () => {
let { settings } = await browser.storage.local.get('settings');
assert.strictEqual(settings.source, 'form')
- })
+ });
it('add blacklist', async () => {
let page = await OptionPage.open(lanthan);
@@ -43,20 +43,20 @@ describe("options form page", () => {
// assert default
let settings = (await browser.storage.local.get('settings')).settings;
- assert.deepStrictEqual(settings.form.blacklist, [])
+ assert.deepStrictEqual(settings.form.blacklist, []);
// add blacklist items
await forms.addBlacklist();
- await forms.setBlacklist(0, 'google.com')
+ await forms.setBlacklist(0, 'google.com');
settings = (await browser.storage.local.get('settings')).settings;
- assert.deepStrictEqual(settings.form.blacklist, ['google.com'])
+ assert.deepStrictEqual(settings.form.blacklist, ['google.com']);
await forms.addBlacklist();
- await forms.setBlacklist(1, 'yahoo.com')
+ await forms.setBlacklist(1, 'yahoo.com');
settings = (await browser.storage.local.get('settings')).settings;
- assert.deepStrictEqual(settings.form.blacklist, ['google.com', 'yahoo.com'])
+ assert.deepStrictEqual(settings.form.blacklist, ['google.com', 'yahoo.com']);
// delete first item
await forms.removeBlackList(0);
diff --git a/e2e/partial_blacklist.test.ts b/e2e/partial_blacklist.test.ts
new file mode 100644
index 0000000..950bb39
--- /dev/null
+++ b/e2e/partial_blacklist.test.ts
@@ -0,0 +1,58 @@
+import * as path from 'path';
+import * as assert from 'assert';
+
+import TestServer from './lib/TestServer';
+import { Builder, Lanthan } from 'lanthan';
+import { WebDriver } from 'selenium-webdriver';
+import Page from './lib/Page';
+import Settings from '../src/shared/settings/Settings';
+import SettingRepository from './lib/SettingRepository';
+
+describe("partial blacklist test", () => {
+ let server = new TestServer().receiveContent('/*',
+ `<!DOCTYPE html><html lang="en"><body style="width:10000px; height:10000px"></body></html>`,
+ );
+ let lanthan: Lanthan;
+ let webdriver: WebDriver;
+ let browser: any;
+
+ before(async() => {
+ lanthan = await Builder
+ .forBrowser('firefox')
+ .spyAddon(path.join(__dirname, '..'))
+ .build();
+ webdriver = lanthan.getWebDriver();
+ browser = lanthan.getWebExtBrowser();
+ await server.start();
+
+ let url = server.url().replace('http://', '');
+ await new SettingRepository(browser).saveJSON(Settings.fromJSON({
+ keymaps: {
+ j: { type: 'scroll.vertically', count: 1 },
+ k: { type: 'scroll.vertically', count: -1 },
+ },
+ blacklist: [
+ { 'url': url, 'keys': ['k'] }
+ ]
+ }));
+ });
+
+ after(async() => {
+ await server.stop();
+ if (lanthan) {
+ await lanthan.quit();
+ }
+ });
+
+ it('should disable keys in the partial blacklist', async () => {
+ let page = await Page.navigateTo(webdriver, server.url('/'));
+
+ await page.sendKeys('j');
+ let scrollY = await page.getScrollY();
+ assert.strictEqual(scrollY, 64);
+
+ await page.sendKeys('k');
+ scrollY = await page.getScrollY();
+ assert.strictEqual(scrollY, 64);
+ });
+});
diff --git a/e2e/settings.ts b/e2e/settings.ts
deleted file mode 100644
index b88caf0..0000000
--- a/e2e/settings.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-export default {
- source: 'json',
- json: `{
- "keymaps": {
- "0": { "type": "scroll.home" },
- ":": { "type": "command.show" },
- "o": { "type": "command.show.open", "alter": false },
- "O": { "type": "command.show.open", "alter": true },
- "t": { "type": "command.show.tabopen", "alter": false },
- "T": { "type": "command.show.tabopen", "alter": true },
- "w": { "type": "command.show.winopen", "alter": false },
- "W": { "type": "command.show.winopen", "alter": true },
- "b": { "type": "command.show.buffer" },
- "a": { "type": "command.show.addbookmark", "alter": true },
- "k": { "type": "scroll.vertically", "count": -1 },
- "j": { "type": "scroll.vertically", "count": 1 },
- "h": { "type": "scroll.horizonally", "count": -1 },
- "l": { "type": "scroll.horizonally", "count": 1 },
- "<C-U>": { "type": "scroll.pages", "count": -0.5 },
- "<C-D>": { "type": "scroll.pages", "count": 0.5 },
- "<C-B>": { "type": "scroll.pages", "count": -1 },
- "<C-F>": { "type": "scroll.pages", "count": 1 },
- "gg": { "type": "scroll.top" },
- "G": { "type": "scroll.bottom" },
- "$": { "type": "scroll.end" },
- "d": { "type": "tabs.close" },
- "D": { "type": "tabs.close", "select": "left" },
- "x$": { "type": "tabs.close.right" },
- "!d": { "type": "tabs.close.force" },
- "u": { "type": "tabs.reopen" },
- "K": { "type": "tabs.prev", "count": 1 },
- "J": { "type": "tabs.next", "count": 1 },
- "gT": { "type": "tabs.prev", "count": 1 },
- "gt": { "type": "tabs.next", "count": 1 },
- "g0": { "type": "tabs.first" },
- "g$": { "type": "tabs.last" },
- "<C-6>": { "type": "tabs.prevsel" },
- "r": { "type": "tabs.reload", "cache": false },
- "R": { "type": "tabs.reload", "cache": true },
- "zp": { "type": "tabs.pin.toggle" },
- "zd": { "type": "tabs.duplicate" },
- "zi": { "type": "zoom.in" },
- "zo": { "type": "zoom.out" },
- "zz": { "type": "zoom.neutral" },
- "f": { "type": "follow.start", "newTab": false },
- "F": { "type": "follow.start", "newTab": true, "background": false },
- "m": { "type": "mark.set.prefix" },
- "'": { "type": "mark.jump.prefix" },
- "H": { "type": "navigate.history.prev" },
- "L": { "type": "navigate.history.next" },
- "[[": { "type": "navigate.link.prev" },
- "]]": { "type": "navigate.link.next" },
- "gu": { "type": "navigate.parent" },
- "gU": { "type": "navigate.root" },
- "gi": { "type": "focus.input" },
- "gf": { "type": "page.source" },
- "gh": { "type": "page.home" },
- "gH": { "type": "page.home", "newTab": true },
- "y": { "type": "urls.yank" },
- "p": { "type": "urls.paste", "newTab": false },
- "P": { "type": "urls.paste", "newTab": true },
- "/": { "type": "find.start" },
- "n": { "type": "find.next" },
- "N": { "type": "find.prev" },
- "<S-Esc>": { "type": "addon.toggle.enabled" }
- },
- "search": {
- "default": "google",
- "engines": {
- "google": "http://127.0.0.1:12321/google?q={}",
- "yahoo": "http://127.0.0.1:12321/yahoo?q={}",
- "bing": "http://127.0.0.1:12321/bind?q={}",
- "duckduckgo": "http://127.0.0.1:12321/duplicate?q={}",
- "twitter": "http://127.0.0.1:12321/twitter?q={}",
- "wikipedia": "http://127.0.0.1:12321/wikipedia?q={}"
- }
- },
- "properties": {
- "hintchars": "abcdefghijklmnopqrstuvwxyz",
- "smoothscroll": false,
- "complete": "sbh"
- },
- "blacklist": [
- ]
-}`,
-};
diff --git a/e2e/zoom.test.ts b/e2e/zoom.test.ts
index 396ddd2..af5cc68 100644
--- a/e2e/zoom.test.ts
+++ b/e2e/zoom.test.ts
@@ -20,7 +20,7 @@ describe("zoom test", () => {
.build();
webdriver = lanthan.getWebDriver();
browser = lanthan.getWebExtBrowser();
- tab = (await browser.tabs.query({}))[0]
+ tab = (await browser.tabs.query({}))[0];
page = await Page.currentContext(webdriver);
});
diff --git a/src/content/controllers/SettingController.ts b/src/content/controllers/SettingController.ts
index 06273a0..e1c7f01 100644
--- a/src/content/controllers/SettingController.ts
+++ b/src/content/controllers/SettingController.ts
@@ -15,7 +15,8 @@ export default class SettingController {
async initSettings(): Promise<void> {
try {
let current = await this.settingUseCase.reload();
- let disabled = current.blacklist.includes(window.location.href);
+ let url = new URL(window.location.href);
+ let disabled = current.blacklist.includesEntireBlacklist(url);
if (disabled) {
this.addonEnabledUseCase.disable();
} else {
diff --git a/src/content/di.ts b/src/content/di.ts
index e18806a..63103a1 100644
--- a/src/content/di.ts
+++ b/src/content/di.ts
@@ -2,6 +2,7 @@
import { AddonEnabledRepositoryImpl } from './repositories/AddonEnabledRepository';
import { AddonIndicatorClientImpl } from './client/AddonIndicatorClient';
+import { AddressRepositoryImpl } from './repositories/AddressRepository';
import { ClipboardRepositoryImpl } from './repositories/ClipboardRepository';
import { ConsoleClientImpl } from './client/ConsoleClient';
import { ConsoleFramePresenterImpl } from './presenters/ConsoleFramePresenter';
@@ -31,6 +32,7 @@ import { container } from 'tsyringe';
container.register('FollowMasterClient', { useValue: new FollowMasterClientImpl(window.top) });
container.register('AddonEnabledRepository', { useClass: AddonEnabledRepositoryImpl });
container.register('AddonIndicatorClient', { useClass: AddonIndicatorClientImpl });
+container.register('AddressRepository', { useClass: AddressRepositoryImpl });
container.register('ClipboardRepository', { useClass: ClipboardRepositoryImpl });
container.register('ConsoleClient', { useClass: ConsoleClientImpl });
container.register('ConsoleFramePresenter', { useClass: ConsoleFramePresenterImpl });
diff --git a/src/content/repositories/AddressRepository.ts b/src/content/repositories/AddressRepository.ts
new file mode 100644
index 0000000..6f9487b
--- /dev/null
+++ b/src/content/repositories/AddressRepository.ts
@@ -0,0 +1,9 @@
+export default interface AddressRepository {
+ getCurrentURL(): URL
+}
+
+export class AddressRepositoryImpl implements AddressRepository {
+ getCurrentURL(): URL {
+ return new URL(window.location.href);
+ }
+}
diff --git a/src/content/usecases/KeymapUseCase.ts b/src/content/usecases/KeymapUseCase.ts
index 495f6d0..67d667d 100644
--- a/src/content/usecases/KeymapUseCase.ts
+++ b/src/content/usecases/KeymapUseCase.ts
@@ -6,6 +6,7 @@ import * as operations from '../../shared/operations';
import Keymaps from '../../shared/settings/Keymaps';
import Key from '../../shared/settings/Key';
import KeySequence from '../../shared/settings/KeySequence';
+import AddressRepository from '../repositories/AddressRepository';
type KeymapEntityMap = Map<KeySequence, operations.Operation>;
@@ -25,11 +26,19 @@ export default class KeymapUseCase {
@inject('AddonEnabledRepository')
private addonEnabledRepository: AddonEnabledRepository,
+
+ @inject('AddressRepository')
+ private addressRepository: AddressRepository,
) {
}
nextOp(key: Key): operations.Operation | null {
let sequence = this.repository.enqueueKey(key);
+ if (sequence.length() === 1 && this.blacklistKey(key)) {
+ // ignore if the input starts with black list keys
+ this.repository.clear();
+ return null;
+ }
let keymaps = this.keymapEntityMap();
let matched = Array.from(keymaps.keys()).filter(
@@ -71,4 +80,10 @@ export default class KeymapUseCase {
) as [KeySequence, operations.Operation][];
return new Map<KeySequence, operations.Operation>(entries);
}
+
+ private blacklistKey(key: Key): boolean {
+ let url = this.addressRepository.getCurrentURL();
+ let blacklist = this.settingRepository.get().blacklist;
+ return blacklist.includeKey(url, key);
+ }
}
diff --git a/src/settings/components/form/BlacklistForm.tsx b/src/settings/components/form/BlacklistForm.tsx
index f352e41..4e96cbf 100644
--- a/src/settings/components/form/BlacklistForm.tsx
+++ b/src/settings/components/form/BlacklistForm.tsx
@@ -2,17 +2,17 @@ import './BlacklistForm.scss';
import AddButton from '../ui/AddButton';
import DeleteButton from '../ui/DeleteButton';
import React from 'react';
-import { BlacklistJSON } from '../../../shared/settings/Blacklist';
+import Blacklist, { BlacklistItem } from '../../../shared/settings/Blacklist';
interface Props {
- value: BlacklistJSON;
- onChange: (value: BlacklistJSON) => void;
+ value: Blacklist;
+ onChange: (value: Blacklist) => void;
onBlur: () => void;
}
class BlacklistForm extends React.Component<Props> {
public static defaultProps: Props = {
- value: [],
+ value: new Blacklist([]),
onChange: () => {},
onBlur: () => {},
};
@@ -20,24 +20,22 @@ class BlacklistForm extends React.Component<Props> {
render() {
return <div className='form-blacklist-form'>
{
- this.props.value
- .map((item, index) => {
- if (typeof item !== 'string') {
- // TODO support partial blacklist;
- return null;
- }
- return <div key={index} className='form-blacklist-form-row'>
- <input data-index={index} type='text' name='url'
- className='column-url' value={item}
- onChange={this.bindValue.bind(this)}
- onBlur={this.props.onBlur}
- />
- <DeleteButton data-index={index} name='delete'
- onClick={this.bindValue.bind(this)}
- onBlur={this.props.onBlur}
- />
- </div>;
- })
+ this.props.value.items.map((item, index) => {
+ if (item.partial) {
+ return null;
+ }
+ return <div key={index} className='form-blacklist-form-row'>
+ <input data-index={index} type='text' name='url'
+ className='column-url' value={item.pattern}
+ onChange={this.bindValue.bind(this)}
+ onBlur={this.props.onBlur}
+ />
+ <DeleteButton data-index={index} name='delete'
+ onClick={this.bindValue.bind(this)}
+ onBlur={this.props.onBlur}
+ />
+ </div>;
+ })
}
<AddButton name='add' style={{ float: 'right' }}
onClick={this.bindValue.bind(this)} />
@@ -47,17 +45,17 @@ class BlacklistForm extends React.Component<Props> {
bindValue(e: any) {
let name = e.target.name;
let index = e.target.getAttribute('data-index');
- let next = this.props.value.slice();
+ let items = this.props.value.items;
if (name === 'url') {
- next[index] = e.target.value;
+ items[index] = new BlacklistItem(e.target.value, false, []);
} else if (name === 'add') {
- next.push('');
+ items.push(new BlacklistItem('', false, []));
} else if (name === 'delete') {
- next.splice(index, 1);
+ items.splice(index, 1);
}
- this.props.onChange(next);
+ this.props.onChange(new Blacklist(items));
if (name === 'delete') {
this.props.onBlur();
}
diff --git a/src/settings/components/form/PartialBlacklistForm.scss b/src/settings/components/form/PartialBlacklistForm.scss
new file mode 100644
index 0000000..caf6f93
--- /dev/null
+++ b/src/settings/components/form/PartialBlacklistForm.scss
@@ -0,0 +1,28 @@
+.form-partial-blacklist-form {
+ @mixin row-base {
+ display: flex;
+
+ .column-url {
+ flex: 5;
+ min-width: 0;
+ }
+ .column-keys {
+ flex: 1;
+ min-width: 0;
+ }
+ .column-delete {
+ flex: 1;
+ min-width: 0;
+ }
+ }
+
+ &-header {
+ @include row-base;
+
+ font-weight: bold;
+ }
+
+ &-row {
+ @include row-base;
+ }
+}
diff --git a/src/settings/components/form/PartialBlacklistForm.tsx b/src/settings/components/form/PartialBlacklistForm.tsx
new file mode 100644
index 0000000..0702913
--- /dev/null
+++ b/src/settings/components/form/PartialBlacklistForm.tsx
@@ -0,0 +1,79 @@
+import './PartialBlacklistForm.scss';
+import AddButton from '../ui/AddButton';
+import DeleteButton from '../ui/DeleteButton';
+import React from 'react';
+import Blacklist, { BlacklistItem } from '../../../shared/settings/Blacklist';
+
+interface Props {
+ value: Blacklist;
+ onChange: (value: Blacklist) => void;
+ onBlur: () => void;
+}
+
+class PartialBlacklistForm extends React.Component<Props> {
+ public static defaultProps: Props = {
+ value: new Blacklist([]),
+ onChange: () => {},
+ onBlur: () => {},
+ };
+
+ render() {
+ return <div className='form-partial-blacklist-form'>
+ <div className='form-partial-blacklist-form-header'>
+ <div className='column-url'>URL</div>
+ <div className='column-keys'>Keys</div>
+ </div>
+ {
+ this.props.value.items.map((item, index) => {
+ if (!item.partial) {
+ return null;
+ }
+ return <div key={index} className='form-partial-blacklist-form-row'>
+ <input data-index={index} type='text' name='url'
+ className='column-url' value={item.pattern}
+ onChange={this.bindValue.bind(this)}
+ onBlur={this.props.onBlur}
+ />
+ <input data-index={index} type='text' name='keys'
+ className='column-keys' value={item.keys.join(',')}
+ onChange={this.bindValue.bind(this)}
+ onBlur={this.props.onBlur}
+ />
+ <DeleteButton data-index={index} name='delete'
+ onClick={this.bindValue.bind(this)}
+ onBlur={this.props.onBlur}
+ />
+ </div>;
+ })
+ }
+ <AddButton name='add' style={{ float: 'right' }}
+ onClick={this.bindValue.bind(this)} />
+ </div>;
+ }
+
+ bindValue(e: any) {
+ let name = e.target.name;
+ let index = e.target.getAttribute('data-index');
+ let items = this.props.value.items;
+
+ if (name === 'url') {
+ let current = items[index];
+ items[index] = new BlacklistItem(e.target.value, true, current.keys);
+ } else if (name === 'keys') {
+ let current = items[index];
+ items[index] = new BlacklistItem(
+ current.pattern, true, e.target.value.split(','));
+ } else if (name === 'add') {
+ items.push(new BlacklistItem('', true, []));
+ } else if (name === 'delete') {
+ items.splice(index, 1);
+ }
+
+ this.props.onChange(new Blacklist(items));
+ if (name === 'delete') {
+ this.props.onBlur();
+ }
+ }
+}
+
+export default PartialBlacklistForm;
diff --git a/src/settings/components/index.tsx b/src/settings/components/index.tsx
index 160dd9c..3eb2dbe 100644
--- a/src/settings/components/index.tsx
+++ b/src/settings/components/index.tsx
@@ -6,6 +6,7 @@ import SearchForm from './form/SearchForm';
import KeymapsForm from './form/KeymapsForm';
import BlacklistForm from './form/BlacklistForm';
import PropertiesForm from './form/PropertiesForm';
+import PartialBlacklistForm from './form/PartialBlacklistForm';
import * as settingActions from '../../settings/actions/setting';
import SettingData, {
FormKeymaps, FormSearch, FormSettings, JSONTextSettings,
@@ -53,7 +54,15 @@ class SettingsComponent extends React.Component<Props> {
<fieldset>
<legend>Blacklist</legend>
<BlacklistForm
- value={form.blacklist.toJSON()}
+ value={form.blacklist}
+ onChange={this.bindBlacklistForm.bind(this)}
+ onBlur={this.save.bind(this)}
+ />
+ </fieldset>
+ <fieldset>
+ <legend>Partial blacklist</legend>
+ <PartialBlacklistForm
+ value={form.blacklist}
onChange={this.bindBlacklistForm.bind(this)}
onBlur={this.save.bind(this)}
/>
@@ -138,11 +147,10 @@ class SettingsComponent extends React.Component<Props> {
this.props.dispatch(settingActions.set(data));
}
- bindBlacklistForm(value: any) {
+ bindBlacklistForm(blacklist: Blacklist) {
let data = new SettingData({
source: this.props.source,
- form: (this.props.form as FormSettings).buildWithBlacklist(
- Blacklist.fromJSON(value)),
+ form: (this.props.form as FormSettings).buildWithBlacklist(blacklist),
});
this.props.dispatch(settingActions.set(data));
}
diff --git a/src/shared/settings/Blacklist.ts b/src/shared/settings/Blacklist.ts
index a95b606..0cfbd71 100644
--- a/src/shared/settings/Blacklist.ts
+++ b/src/shared/settings/Blacklist.ts
@@ -1,39 +1,125 @@
-export type BlacklistJSON = string[];
+import Key from './Key';
-const fromWildcard = (pattern: string): RegExp => {
+export type BlacklistItemJSON = string | {
+ url: string,
+ keys: string[],
+};
+
+export type BlacklistJSON = BlacklistItemJSON[];
+
+const regexFromWildcard = (pattern: string): RegExp => {
let regexStr = '^' + pattern.replace(/\*/g, '.*') + '$';
return new RegExp(regexStr);
};
+const isArrayOfString = (raw: any): boolean => {
+ if (!Array.isArray(raw)) {
+ return false;
+ }
+ for (let x of Array.from(raw)) {
+ if (typeof x !== 'string') {
+ return false;
+ }
+ }
+ return true;
+};
+
+export class BlacklistItem {
+ public readonly pattern: string;
+
+ private regex: RegExp;
+
+ public readonly partial: boolean;
+
+ public readonly keys: string[];
+
+ private readonly keyEntities: Key[];
+
+ constructor(
+ pattern: string,
+ partial: boolean,
+ keys: string[]
+ ) {
+ this.pattern = pattern;
+ this.regex = regexFromWildcard(pattern);
+ this.partial = partial;
+ this.keys = keys;
+ this.keyEntities = this.keys.map(Key.fromMapKey);
+ }
+
+ static fromJSON(raw: any): BlacklistItem {
+ if (typeof raw === 'string') {
+ return new BlacklistItem(raw, false, []);
+ } else if (typeof raw === 'object' && raw !== null) {
+ if (!('url' in raw)) {
+ throw new TypeError(
+ `missing field "url" of blacklist item: ${JSON.stringify(raw)}`);
+ }
+ if (typeof raw.url !== 'string') {
+ throw new TypeError(
+ `invalid field "url" of blacklist item: ${JSON.stringify(raw)}`);
+ }
+ if (!('keys' in raw)) {
+ throw new TypeError(
+ `missing field "keys" of blacklist item: ${JSON.stringify(raw)}`);
+ }
+ if (!isArrayOfString(raw.keys)) {
+ throw new TypeError(
+ `invalid field "keys" of blacklist item: ${JSON.stringify(raw)}`);
+ }
+ return new BlacklistItem(raw.url as string, true, raw.keys as string[]);
+ }
+ throw new TypeError(
+ `invalid format of blacklist item: ${JSON.stringify(raw)}`);
+ }
+
+ toJSON(): BlacklistItemJSON {
+ if (!this.partial) {
+ return this.pattern;
+ }
+ return { url: this.pattern, keys: this.keys };
+ }
+
+ matches(url: URL): boolean {
+ return this.pattern.includes('/')
+ ? this.regex.test(url.host + url.pathname)
+ : this.regex.test(url.host);
+ }
+
+ includeKey(url: URL, key: Key): boolean {
+ if (!this.matches(url)) {
+ return false;
+ }
+ if (!this.partial) {
+ return true;
+ }
+ return this.keyEntities.some(k => k.equals(key));
+ }
+}
+
export default class Blacklist {
constructor(
- private blacklist: string[],
+ public readonly items: BlacklistItem[],
) {
}
static fromJSON(json: any): Blacklist {
if (!Array.isArray(json)) {
- throw new TypeError(`"blacklist" is not an array of string`);
+ throw new TypeError('blacklist is not an array: ' + JSON.stringify(json));
}
- for (let x of json) {
- if (typeof x !== 'string') {
- throw new TypeError(`"blacklist" is not an array of string`);
- }
- }
- return new Blacklist(json);
+ let items = Array.from(json).map(item => BlacklistItem.fromJSON(item));
+ return new Blacklist(items);
}
toJSON(): BlacklistJSON {
- return this.blacklist;
+ return this.items.map(item => item.toJSON());
}
- includes(url: string): boolean {
- let u = new URL(url);
- return this.blacklist.some((item) => {
- if (!item.includes('/')) {
- return fromWildcard(item).test(u.host);
- }
- return fromWildcard(item).test(u.host + u.pathname);
- });
+ includesEntireBlacklist(url: URL): boolean {
+ return this.items.some(item => !item.partial && item.matches(url));
+ }
+
+ includeKey(url: URL, key: Key) {
+ return this.items.some(item => item.includeKey(url, key));
}
}
diff --git a/src/shared/settings/KeySequence.ts b/src/shared/settings/KeySequence.ts
index 4955583..abae61a 100644
--- a/src/shared/settings/KeySequence.ts
+++ b/src/shared/settings/KeySequence.ts
@@ -1,4 +1,4 @@
-import Key from '../../shared/settings/Key';
+import Key from './Key';
export default class KeySequence {
constructor(
diff --git a/test/content/usecases/KeymapUseCase.test.ts b/test/content/usecases/KeymapUseCase.test.ts
new file mode 100644
index 0000000..5f2feba
--- /dev/null
+++ b/test/content/usecases/KeymapUseCase.test.ts
@@ -0,0 +1,133 @@
+import KeymapUseCase from '../../../src/content/usecases/KeymapUseCase';
+import {expect} from 'chai';
+import SettingRepository from "../../../src/content/repositories/SettingRepository";
+import Settings from "../../../src/shared/settings/Settings";
+import AddonEnabledRepository from "../../../src/content/repositories/AddonEnabledRepository";
+import {KeymapRepositoryImpl} from "../../../src/content/repositories/KeymapRepository";
+import Key from "../../../src/shared/settings/Key";
+import AddressRepository from "../../../src/content/repositories/AddressRepository";
+
+class MockSettingRepository implements SettingRepository {
+ constructor(
+ private readonly settings: Settings,
+ ) {
+ }
+
+ get(): Settings {
+ return this.settings;
+ }
+
+ set(_setting: Settings): void {
+ throw new Error('TODO');
+ }
+}
+
+class MockAddonEnabledRepository implements AddonEnabledRepository {
+ constructor(
+ private readonly enabled: boolean,
+ ) {
+ }
+
+ get(): boolean {
+ return this.enabled;
+ }
+
+ set(_on: boolean): void {
+ throw new Error('TODO');
+ }
+}
+
+class MockAddressRepository implements AddressRepository {
+ constructor(
+ private url: URL,
+ ) {
+ }
+
+ getCurrentURL(): URL {
+ return this.url;
+ }
+}
+
+
+describe('KeymapUseCase', () => {
+ it('returns matched operation', () => {
+ let settings = Settings.fromJSON({
+ keymaps: {
+ k: {type: 'scroll.vertically', count: -1},
+ j: {type: 'scroll.vertically', count: 1},
+ gg: {type: 'scroll.top'},
+ },
+ });
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1});
+ expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1});
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'});
+ expect(sut.nextOp(Key.fromMapKey('z'))).to.be.null;
+ });
+
+ it('returns only ADDON_ENABLE and ADDON_TOGGLE_ENABLED operation', () => {
+ let settings = Settings.fromJSON({
+ keymaps: {
+ k: {type: 'scroll.vertically', count: -1},
+ a: {type: 'addon.enable'},
+ b: {type: 'addon.toggle.enabled'},
+ },
+ });
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(false),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('a'))).to.deep.equal({type: 'addon.enable'});
+ expect(sut.nextOp(Key.fromMapKey('b'))).to.deep.equal({type: 'addon.toggle.enabled'});
+ });
+
+ it('blocks keys in the partial blacklist', () => {
+ let settings = Settings.fromJSON({
+ keymaps: {
+ k: {type: 'scroll.vertically', count: -1},
+ j: {type: 'scroll.vertically', count: 1},
+ gg: {"type": "scroll.top"},
+ G: {"type": "scroll.bottom"},
+ },
+ blacklist: [
+ { url: "example.com", keys: ['g'] },
+ { url: "example.org", keys: ['<S-G>'] }
+ ],
+ });
+
+ let sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.com')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('k'))).to.deep.equal({type: 'scroll.vertically', count: -1});
+ expect(sut.nextOp(Key.fromMapKey('j'))).to.deep.equal({type: 'scroll.vertically', count: 1});
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('G'))).to.deep.equal({type: 'scroll.bottom'});
+
+ sut = new KeymapUseCase(
+ new KeymapRepositoryImpl(),
+ new MockSettingRepository(settings),
+ new MockAddonEnabledRepository(true),
+ new MockAddressRepository(new URL('https://example.org')),
+ );
+
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.be.null;
+ expect(sut.nextOp(Key.fromMapKey('g'))).to.deep.equal({type: 'scroll.top'});
+ expect(sut.nextOp(Key.fromMapKey('G'))).to.be.null;
+ });
+});
diff --git a/test/settings/components/form/BlacklistForm.test.tsx b/test/settings/components/form/BlacklistForm.test.tsx
index 2be5d96..7daf513 100644
--- a/test/settings/components/form/BlacklistForm.test.tsx
+++ b/test/settings/components/form/BlacklistForm.test.tsx
@@ -2,13 +2,16 @@ import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestRenderer from 'react-test-renderer';
import ReactTestUtils from 'react-dom/test-utils';
-import BlacklistForm from 'settings/components/form/BlacklistForm'
+import { expect } from 'chai'
+
+import BlacklistForm from '../../../../src/settings/components/form/BlacklistForm'
+import Blacklist from '../../../../src/shared/settings/Blacklist';
describe("settings/form/BlacklistForm", () => {
describe('render', () => {
it('renders BlacklistForm', () => {
let root = ReactTestRenderer.create(
- <BlacklistForm value={['*.slack.com', 'www.google.com/maps']} />,
+ <BlacklistForm value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps'])} />,
).root;
let children = root.children[0].children;
@@ -43,10 +46,10 @@ describe("settings/form/BlacklistForm", () => {
it('invokes onChange event on edit', (done) => {
ReactTestUtils.act(() => {
ReactDOM.render(<BlacklistForm
- value={['*.slack.com', 'www.google.com/maps*']}
+ value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps*'])}
onChange={value => {
- expect(value).to.have.lengthOf(2);
- expect(value).to.have.members(['gitter.im', 'www.google.com/maps*']);
+ let urls = value.items.map(item => item.pattern);
+ expect(urls).to.have.members(['gitter.im', 'www.google.com/maps*']);
done();
}}
/>, container)
@@ -60,10 +63,10 @@ describe("settings/form/BlacklistForm", () => {
it('invokes onChange event on delete', (done) => {
ReactTestUtils.act(() => {
ReactDOM.render(<BlacklistForm
- value={['*.slack.com', 'www.google.com/maps*']}
+ value={Blacklist.fromJSON(['*.slack.com', 'www.google.com/maps*'])}
onChange={value => {
- expect(value).to.have.lengthOf(1);
- expect(value).to.have.members(['www.google.com/maps*']);
+ let urls = value.items.map(item => item.pattern);
+ expect(urls).to.have.members(['www.google.com/maps*']);
done();
}}
/>, container)
@@ -76,10 +79,10 @@ describe("settings/form/BlacklistForm", () => {
it('invokes onChange event on add', (done) => {
ReactTestUtils.act(() => {
ReactDOM.render(<BlacklistForm
- value={['*.slack.com']}
+ value={Blacklist.fromJSON(['*.slack.com'])}
onChange={value => {
- expect(value).to.have.lengthOf(2);
- expect(value).to.have.members(['*.slack.com', '']);
+ let urls = value.items.map(item => item.pattern);
+ expect(urls).to.have.members(['*.slack.com', '']);
done();
}}
/>, container);
diff --git a/test/shared/settings/Blacklist.test.ts b/test/shared/settings/Blacklist.test.ts
index fbacf5d..133112c 100644
--- a/test/shared/settings/Blacklist.test.ts
+++ b/test/shared/settings/Blacklist.test.ts
@@ -1,77 +1,158 @@
-import Blacklist from '../../../src/shared/settings/Blacklist';
+import Blacklist, { BlacklistItem } from '../../../src/shared/settings/Blacklist';
import { expect } from 'chai';
-
-describe('Blacklist', () => {
- describe('fromJSON', () => {
- it('returns empty array by empty settings', () => {
- let blacklist = Blacklist.fromJSON([]);
- expect(blacklist.toJSON()).to.be.empty;
+import Key from '../../../src/shared/settings/Key';
+
+describe('BlacklistItem', () => {
+ describe('#fromJSON', () => {
+ it('parses string pattern', () => {
+ let item = BlacklistItem.fromJSON('example.com');
+ expect(item.pattern).to.equal('example.com');
+ expect(item.partial).to.be.false;
});
- it('returns blacklist by valid settings', () => {
- let blacklist = Blacklist.fromJSON([
- 'github.com',
- 'circleci.com',
- ]);
-
- expect(blacklist.toJSON()).to.deep.equal([
- 'github.com',
- 'circleci.com',
- ]);
+ it('parses partial blacklist item', () => {
+ let item = BlacklistItem.fromJSON({ url: 'example.com', keys: ['j', 'k']});
+ expect(item.pattern).to.equal('example.com');
+ expect(item.partial).to.be.true;
+ expect(item.keys).to.deep.equal(['j', 'k']);
});
- it('throws a TypeError by invalid settings', () => {
- expect(() => Blacklist.fromJSON(null)).to.throw(TypeError);
- expect(() => Blacklist.fromJSON({})).to.throw(TypeError);
- expect(() => Blacklist.fromJSON([1,2,3])).to.throw(TypeError);
+ it('throws a TypeError', () => {
+ expect(() => BlacklistItem.fromJSON(null)).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON(100)).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON({})).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON({url: 'google.com'})).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON({keys: ['a']})).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: 10})).to.throw(TypeError);
+ expect(() => BlacklistItem.fromJSON({url: 'google.com', keys: ['a', 'b', 3]})).to.throw(TypeError);
});
});
- describe('#includes', () => {
- it('matches by *', () => {
- let blacklist = new Blacklist(['*']);
-
- expect(blacklist.includes('https://github.com/abc')).to.be.true;
+ describe('#matches', () => {
+ it('matches by "*"', () => {
+ let item = BlacklistItem.fromJSON('*');
+ expect(item.matches(new URL('https://github.com/abc'))).to.be.true;
});
it('matches by hostname', () => {
- let blacklist = new Blacklist(['github.com']);
-
- expect(blacklist.includes('https://github.com')).to.be.true;
- expect(blacklist.includes('https://gist.github.com')).to.be.false;
- expect(blacklist.includes('https://github.com/ueokande')).to.be.true;
- expect(blacklist.includes('https://github.org')).to.be.false;
- expect(blacklist.includes('https://google.com/search?q=github.org')).to.be.false;
+ let item = BlacklistItem.fromJSON('github.com');
+ expect(item.matches(new URL('https://github.com'))).to.be.true;
+ expect(item.matches(new URL('https://gist.github.com'))).to.be.false;
+ expect(item.matches(new URL('https://github.com/ueokande'))).to.be.true;
+ expect(item.matches(new URL('https://github.org'))).to.be.false;
+ expect(item.matches(new URL('https://google.com/search?q=github.org'))).to.be.false;
});
it('matches by hostname with wildcard', () => {
- let blacklist = new Blacklist(['*.github.com']);
+ let item = BlacklistItem.fromJSON('*.github.com');
- expect(blacklist.includes('https://github.com')).to.be.false;
- expect(blacklist.includes('https://gist.github.com')).to.be.true;
- })
+ expect(item.matches(new URL('https://github.com'))).to.be.false;
+ expect(item.matches(new URL('https://gist.github.com'))).to.be.true;
+ });
it('matches by path', () => {
- let blacklist = new Blacklist(['github.com/abc']);
+ let item = BlacklistItem.fromJSON('github.com/abc');
- expect(blacklist.includes('https://github.com/abc')).to.be.true;
- expect(blacklist.includes('https://github.com/abcdef')).to.be.false;
- expect(blacklist.includes('https://gist.github.com/abc')).to.be.false;
- })
+ expect(item.matches(new URL('https://github.com/abc'))).to.be.true;
+ expect(item.matches(new URL('https://github.com/abcdef'))).to.be.false;
+ expect(item.matches(new URL('https://gist.github.com/abc'))).to.be.false;
+ });
it('matches by path with wildcard', () => {
- let blacklist = new Blacklist(['github.com/abc*']);
+ let item = BlacklistItem.fromJSON('github.com/abc*');
- expect(blacklist.includes('https://github.com/abc')).to.be.true;
- expect(blacklist.includes('https://github.com/abcdef')).to.be.true;
- expect(blacklist.includes('https://gist.github.com/abc')).to.be.false;
- })
+ expect(item.matches(new URL('https://github.com/abc'))).to.be.true;
+ expect(item.matches(new URL('https://github.com/abcdef'))).to.be.true;
+ expect(item.matches(new URL('https://gist.github.com/abc'))).to.be.false;
+ });
it('matches address and port', () => {
- let blacklist = new Blacklist(['127.0.0.1:8888']);
+ let item = BlacklistItem.fromJSON('127.0.0.1:8888');
+
+ expect(item.matches(new URL('http://127.0.0.1:8888/'))).to.be.true;
+ expect(item.matches(new URL('http://127.0.0.1:8888/hello'))).to.be.true;
+ });
+
+ it('matches with partial blacklist', () => {
+ let item = BlacklistItem.fromJSON({ url: 'google.com', keys: ['j', 'k'] });
+
+ expect(item.matches(new URL('https://google.com'))).to.be.true;
+ expect(item.matches(new URL('https://yahoo.com'))).to.be.false;
+ })
+ });
+
+ describe('#includesPartialKeys', () => {
+ it('matches with partial keys', () => {
+ let item = BlacklistItem.fromJSON({url: 'google.com', keys: ['j', 'k', '<C-U>']});
+
+ expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('j'))).to.be.true;
+ expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('<C-U>'))).to.be.true;
+ expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('z'))).to.be.false;
+ expect(item.includeKey(new URL('http://google.com/maps'), Key.fromMapKey('u'))).to.be.false;
+ expect(item.includeKey(new URL('http://maps.google.com/'), Key.fromMapKey('j'))).to.be.false;
+ })
+ });
+});
+
+describe('Blacklist', () => {
+ describe('#fromJSON', () => {
+ it('parses string list', () => {
+ let blacklist = Blacklist.fromJSON(['example.com', 'example.org']);
+ expect(blacklist.toJSON()).to.deep.equals([
+ 'example.com', 'example.org',
+ ]);
+ });
+
+ it('parses mixed blacklist', () => {
+ let blacklist = Blacklist.fromJSON([
+ { url: 'example.com', keys: ['j', 'k']},
+ 'example.org',
+ ]);
+ expect(blacklist.toJSON()).to.deep.equals([
+ { url: 'example.com', keys: ['j', 'k']},
+ 'example.org',
+ ]);
+ });
- expect(blacklist.includes('http://127.0.0.1:8888/')).to.be.true;
- expect(blacklist.includes('http://127.0.0.1:8888/hello')).to.be.true;
+ it('parses empty blacklist', () => {
+ let blacklist = Blacklist.fromJSON([]);
+ expect(blacklist.toJSON()).to.deep.equals([]);
+ });
+
+ it('throws a TypeError', () => {
+ expect(() => Blacklist.fromJSON(null)).to.throw(TypeError);
+ expect(() => Blacklist.fromJSON(100)).to.throw(TypeError);
+ expect(() => Blacklist.fromJSON({})).to.throw(TypeError);
+ expect(() => Blacklist.fromJSON([100])).to.throw(TypeError);
+ expect(() => Blacklist.fromJSON([{}])).to.throw(TypeError);
})
- })
+ });
+
+ describe('#includesEntireBlacklist', () => {
+ it('matches a url with entire blacklist', () => {
+ let blacklist = Blacklist.fromJSON(['google.com', '*.github.com']);
+ expect(blacklist.includesEntireBlacklist(new URL('https://google.com'))).to.be.true;
+ expect(blacklist.includesEntireBlacklist(new URL('https://github.com'))).to.be.false;
+ expect(blacklist.includesEntireBlacklist(new URL('https://gist.github.com'))).to.be.true;
+ });
+
+ it('does not matches with partial blacklist', () => {
+ let blacklist = Blacklist.fromJSON(['google.com', { url: 'yahoo.com', keys: ['j', 'k'] }]);
+ expect(blacklist.includesEntireBlacklist(new URL('https://google.com'))).to.be.true;
+ expect(blacklist.includesEntireBlacklist(new URL('https://yahoo.com'))).to.be.false;
+ });
+ });
+
+ describe('#includesKeys', () => {
+ it('matches with entire blacklist or keys in the partial blacklist', () => {
+ let blacklist = Blacklist.fromJSON([
+ 'google.com',
+ { url: 'github.com', keys: ['j', 'k'] },
+ ]);
+
+ expect(blacklist.includeKey(new URL('https://google.com'), Key.fromMapKey('j'))).to.be.true;
+ expect(blacklist.includeKey(new URL('https://github.com'), Key.fromMapKey('j'))).to.be.true;
+ expect(blacklist.includeKey(new URL('https://github.com'), Key.fromMapKey('a'))).to.be.false;
+ });
+ });
});