Skip to content

Commit

Permalink
Merge pull request #434 from hogashi/chrome-storage
Browse files Browse the repository at this point in the history
use chrome.storage as options storage
  • Loading branch information
hogashi authored Jan 9, 2023
2 parents b586095 + b627845 commit e7fc00b
Show file tree
Hide file tree
Showing 22 changed files with 337 additions and 222 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ jobs:
run: yarn test
env:
CI: true
# for jest
FORCE_COLOR: true
12 changes: 6 additions & 6 deletions __tests__/ButtonSetter.test.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// import * as main from '../src/main';
import { SHOW_ON_TIMELINE, isFalse, SHOW_ON_TWEET_DETAIL } from '../src/constants';
import { SHOW_ON_TIMELINE, isFalse, SHOW_ON_TWEET_DETAIL, isTrue } from '../src/constants';
import { ButtonSetter } from '../src/ButtonSetter';

function makeAllEnabledOptions() {
return {
SHOW_ON_TIMELINE: 'istrue',
SHOW_ON_TWEET_DETAIL: 'istrue',
SHOW_ON_TWEETDECK_TIMELINE: 'istrue',
SHOW_ON_TWEETDECK_TWEET_DETAIL: 'istrue',
STRIP_IMAGE_SUFFIX: 'istrue',
SHOW_ON_TIMELINE: isTrue,
SHOW_ON_TWEET_DETAIL: isTrue,
SHOW_ON_TWEETDECK_TIMELINE: isTrue,
SHOW_ON_TWEETDECK_TWEET_DETAIL: isTrue,
STRIP_IMAGE_SUFFIX: isTrue,
};
}

Expand Down
12 changes: 6 additions & 6 deletions __tests__/ButtonSetterTweetDeck.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { SHOW_ON_TWEETDECK_TIMELINE, isFalse, SHOW_ON_TWEETDECK_TWEET_DETAIL } from '../src/constants';
import { SHOW_ON_TWEETDECK_TIMELINE, isFalse, SHOW_ON_TWEETDECK_TWEET_DETAIL, isTrue } from '../src/constants';
import { ButtonSetterTweetDeck } from '../src/ButtonSetterTweetDeck';

function makeAllEnabledOptions() {
return {
SHOW_ON_TIMELINE: 'istrue',
SHOW_ON_TWEET_DETAIL: 'istrue',
SHOW_ON_TWEETDECK_TIMELINE: 'istrue',
SHOW_ON_TWEETDECK_TWEET_DETAIL: 'istrue',
STRIP_IMAGE_SUFFIX: 'istrue',
SHOW_ON_TIMELINE: isTrue,
SHOW_ON_TWEET_DETAIL: isTrue,
SHOW_ON_TWEETDECK_TIMELINE: isTrue,
SHOW_ON_TWEETDECK_TWEET_DETAIL: isTrue,
STRIP_IMAGE_SUFFIX: isTrue,
};
}

Expand Down
14 changes: 7 additions & 7 deletions __tests__/Constants.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
isFalse,
OPTION_KEYS,
OPTIONS_TEXT,
userjsOptions,
initialOptions,
isTwitter,
isTweetdeck,
isImageTab,
Expand Down Expand Up @@ -49,15 +49,15 @@ describe('定数', () => {
});

it('userjs用の設定項目の初期値は全部真', () => {
expect(userjsOptions).toStrictEqual({
expect(initialOptions).toStrictEqual({
// 公式Web
SHOW_ON_TIMELINE: 'istrue',
SHOW_ON_TWEET_DETAIL: 'istrue',
SHOW_ON_TIMELINE: isTrue,
SHOW_ON_TWEET_DETAIL: isTrue,
// TweetDeck
SHOW_ON_TWEETDECK_TIMELINE: 'istrue',
SHOW_ON_TWEETDECK_TWEET_DETAIL: 'istrue',
SHOW_ON_TWEETDECK_TIMELINE: isTrue,
SHOW_ON_TWEETDECK_TWEET_DETAIL: isTrue,
// 画像ページ
STRIP_IMAGE_SUFFIX: 'istrue',
STRIP_IMAGE_SUFFIX: isTrue,
});

expect(OPTION_KEYS).toStrictEqual([
Expand Down
42 changes: 13 additions & 29 deletions __tests__/Utils.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// import * as main from '../src/main';
import { isTrue, isFalse, OPTION_KEYS } from '../src/constants';

import { chrome } from 'jest-chrome';

import { OPTION_KEYS, initialOptionsBool } from '../src/constants';
import {
printException,
collectUrlParams,
Expand Down Expand Up @@ -244,40 +247,25 @@ describe('Utils', () => {

describe('updateOptions', () => {
describe('Chrome拡張機能のとき', () => {
const originalChrome = window.chrome;
beforeAll(() => {
delete window.chrome;
window.chrome = { runtime: { id: 'id' } };
});
afterAll(() => {
window.chrome = originalChrome;
});
chrome.runtime.id = 'mock';

it('初期設定を取得できる', async () => {
const expected = {};
OPTION_KEYS.forEach((key) => {
expected[key] = isTrue;
});
window.chrome.runtime.sendMessage = jest.fn((_, callback) => callback({ data: {} }));
await expect(updateOptions()).resolves.toStrictEqual(expected);
chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({ data: initialOptionsBool }));
await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool);
});

it('設定した値を取得できる', async () => {
const expected = {};
const expected = { ...initialOptionsBool };
OPTION_KEYS.forEach((key, i) => {
expected[key] = i % 2 === 0 ? isTrue : isFalse;
expected[key] = i % 2 === 0;
});
window.chrome.runtime.sendMessage = jest.fn((_, callback) => callback({ data: { ...expected } }));
chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({ data: { ...expected } }));
await expect(updateOptions()).resolves.toStrictEqual(expected);
});

it('設定が取得できなかったら初期設定', async () => {
const expected = {};
OPTION_KEYS.forEach((key) => {
expected[key] = isTrue;
});
window.chrome.runtime.sendMessage = jest.fn((_, callback) => callback({}));
await expect(updateOptions()).resolves.toStrictEqual(expected);
chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({}));
await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool);
});
});

Expand All @@ -292,11 +280,7 @@ describe('Utils', () => {
});

it('初期設定を取得できる', async () => {
const expected = {};
OPTION_KEYS.forEach((key) => {
expected[key] = isTrue;
});
await expect(updateOptions()).resolves.toStrictEqual(expected);
await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool);
});
});
});
Expand Down
73 changes: 73 additions & 0 deletions __tests__/options.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { chrome } from 'jest-chrome';
import {
initialOptions,
initialOptionsBool,
isFalse,
MIGRATED_TO_CHROME_STORAGE,
SHOW_ON_TWEETDECK_TIMELINE,
SHOW_ON_TIMELINE,
SHOW_ON_TWEETDECK_TWEET_DETAIL,
} from '../src/constants';

import { getOptions, setOptions } from '../src/extension-contexts/options';

let chromeStorage = {};
chrome.storage.sync.set.mockImplementation((items) => {
chromeStorage = { ...chromeStorage, ...items };
});
chrome.storage.sync.get.mockImplementation((keys, callback) => {
if (typeof keys === 'string') {
callback({ [keys]: chromeStorage[keys] });
} else {
callback(Object.fromEntries(Object.entries(chromeStorage).filter(([k, _]) => keys.find((key) => k === key))));
}
});
beforeEach(() => {
chromeStorage = {};
localStorage.clear();
});

describe('options', () => {
describe('getOptions', () => {
it('何もない状態で呼んだら, 初期値が返って, 初期値が保存されて, 移行済みになる', () => {
expect(getOptions()).resolves.toMatchObject(initialOptionsBool);
expect(chromeStorage).toMatchObject({
...initialOptionsBool,
[MIGRATED_TO_CHROME_STORAGE]: true,
});
});
it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => {
Object.entries({ ...initialOptions, [SHOW_ON_TWEETDECK_TIMELINE]: isFalse }).map(([k, v]) => {
localStorage.setItem(k, v);
});
const expected = { ...initialOptionsBool, [SHOW_ON_TWEETDECK_TIMELINE]: false };
expect(getOptions()).resolves.toMatchObject(expected);
expect(chromeStorage).toMatchObject({
...expected,
[MIGRATED_TO_CHROME_STORAGE]: true,
});
});
it('移行済みなら, 保存された設定が返る', () => {
Object.entries({ ...initialOptions, [SHOW_ON_TWEETDECK_TIMELINE]: isFalse }).map(([k, v]) => {
localStorage.setItem(k, v);
});
chromeStorage = {
...initialOptionsBool,
[SHOW_ON_TIMELINE]: false,
[MIGRATED_TO_CHROME_STORAGE]: true,
};
const expected = { ...initialOptionsBool, [SHOW_ON_TIMELINE]: false };
expect(getOptions()).resolves.toMatchObject(expected);
expect(chromeStorage).toMatchObject({
...expected,
[MIGRATED_TO_CHROME_STORAGE]: true,
});
});
});
describe('setOptions', () => {
const expected = { ...initialOptionsBool, [SHOW_ON_TWEETDECK_TWEET_DETAIL]: false };
setOptions(expected);
expect(chrome.storage.sync.set.mock.calls.length).toBe(1);
expect(chrome.storage.sync.set.mock.lastCall[0]).toBe(expected);
});
});
101 changes: 48 additions & 53 deletions __tests__/popup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,28 @@ import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
const { shallow } = Enzyme;
import { chrome } from 'jest-chrome';

import {
OPTIONS_TEXT,
SHOW_ON_TIMELINE,
isFalse,
SHOW_ON_TWEETDECK_TIMELINE,
isTrue,
OPTION_KEYS,
initialOptionsBool,
} from '../src/constants';
import { Popup } from '../src/popup';
import { Popup } from '../src/extension-contexts/popup';

let mockOptions = initialOptionsBool;
chrome.storage.sync.set.mockImplementation((newOptions) => {
mockOptions = { ...newOptions };
});
chrome.storage.sync.get.mockImplementation(() => mockOptions);

describe('Popup', () => {
it('render', () => {
const optionsText = OPTIONS_TEXT;
const optionKeys = OPTION_KEYS;
const optionsEnabled = {};
optionKeys.forEach((key) => {
optionsEnabled[key] = true;
});
const optionsEnabled = { ...initialOptionsBool };

const props = {
optionsText,
Expand All @@ -33,84 +36,76 @@ describe('Popup', () => {
});

describe('保存ボタン押すと設定が保存される', () => {
window.localStorage = {};
it('最初は空', () => {
expect(window.localStorage).toMatchObject({});
});
mockOptions = initialOptionsBool;

const optionsText = OPTIONS_TEXT;
const optionKeys = OPTION_KEYS;
const optionsEnabled = {};
const expectOptions = {};
optionKeys.forEach((key) => {
optionsEnabled[key] = true;
expectOptions[key] = isTrue;
});
const optionsEnabled = { ...initialOptionsBool };
// 初期設定いっこOFFにしてみる
optionsEnabled[SHOW_ON_TIMELINE] = false;
expectOptions[SHOW_ON_TIMELINE] = isFalse;

const props = {
optionsText,
optionKeys,
optionsEnabled,
};

window.chrome = {
tabs: {
query: jest.fn((_, callback) => {
callback([
{
// 対象タブ
id: 1,
url: 'http://twitter.com',
},
{
// 対象ではないタブ
id: 1,
url: 'http://google.com',
},
{
// 対象ではないタブ
id: 1,
},
{
// 対象ではないタブ
url: 'http://twitter.com',
},
]);
}),
sendMessage: jest.fn((id, option, callback) => {
callback('mock ok');
}),
},
};
chrome.tabs.query.mockImplementation((_, callback) => {
callback([
{
// 対象タブ
id: 1,
url: 'http://twitter.com',
},
{
// 対象ではないタブ
id: 1,
url: 'http://google.com',
},
{
// 対象ではないタブ
id: 1,
},
{
// 対象ではないタブ
url: 'http://twitter.com',
},
]);
});
chrome.tabs.sendMessage.mockImplementation((id, option, callback) => {
callback('mock ok');
});
const wrapper = shallow(<Popup {...props} />);

it('渡した設定がそのまま保存される', () => {
wrapper.find('.saveSettingButton').simulate('click');
// 送りたいタブは正しい形式かつ対象ホストなタブのみ
expect(window.chrome.tabs.query.mock.calls.length).toBe(1);

expect(window.localStorage).toMatchObject(expectOptions);
expect(mockOptions).toMatchObject(optionsEnabled);
});

it('チェックボックスをクリックして保存すると設定変えられる', () => {
wrapper.find(`.${SHOW_ON_TIMELINE}`).simulate('click');
wrapper.find(`.${SHOW_ON_TWEETDECK_TIMELINE}`).simulate('click');
expectOptions[SHOW_ON_TIMELINE] = isTrue;
expectOptions[SHOW_ON_TWEETDECK_TIMELINE] = isFalse;

wrapper.find('.saveSettingButton').simulate('click');
expect(window.localStorage).toMatchObject(expectOptions);
expect(mockOptions).toMatchObject({
...optionsEnabled,
[SHOW_ON_TIMELINE]: true,
[SHOW_ON_TWEETDECK_TIMELINE]: false,
});
});

it('何度も設定変えられる', () => {
wrapper.find(`.${SHOW_ON_TIMELINE}`).simulate('click');
expectOptions[SHOW_ON_TIMELINE] = isFalse;

wrapper.find('.saveSettingButton').simulate('click');
expect(window.localStorage).toMatchObject(expectOptions);
expect(mockOptions).toMatchObject({
...optionsEnabled,
[SHOW_ON_TIMELINE]: false,
[SHOW_ON_TWEETDECK_TIMELINE]: false,
});
});
});
});
2 changes: 1 addition & 1 deletion coverage/badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion dist/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "4.1.0",
"description": "twitterの画像ツイートにボタンを追加する拡張機能。追加されたボタンを押すとツイートの画像を原寸で新しいタブに表示する。連絡先: @hogextend",
"author": "hogashi",
"permissions": ["tabs"],
"permissions": ["tabs", "storage"],
"icons": {
"16": "icons/icon.png",
"48": "icons/icon.png",
Expand Down
Loading

0 comments on commit e7fc00b

Please sign in to comment.