Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use chrome.storage as options storage #434

Merged
merged 38 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0fcd96d
rename var
hogashi Jan 3, 2023
db978fb
options getter
hogashi Jan 3, 2023
0b3de9f
add jest-chrome, add jest config
hogashi Jan 3, 2023
06dddfb
add options test
hogashi Jan 3, 2023
4685c53
use boolean version
hogashi Jan 3, 2023
22e4867
call callback in mock
hogashi Jan 3, 2023
1b41b10
suppose that chrome.storage holds boolean
hogashi Jan 3, 2023
1ee0e02
use var
hogashi Jan 3, 2023
7e14df5
use boolean options
hogashi Jan 3, 2023
6598455
use var in tests
hogashi Jan 3, 2023
5a4c341
chrome.storage must be used in background
hogashi Jan 3, 2023
b3aefb3
add TODO
hogashi Jan 3, 2023
463a7ca
migrate options to chrome storage when get
hogashi Jan 4, 2023
afe32ae
split out options getter
hogashi Jan 4, 2023
eeb848d
use getter
hogashi Jan 4, 2023
1eaf9be
newline at EOF
hogashi Jan 4, 2023
47af79e
move extension contexts scripts
hogashi Jan 4, 2023
a387d2a
fix tests for utils
hogashi Jan 4, 2023
07d1155
remove unused imports
hogashi Jan 4, 2023
53fec06
use jest-chrome mock
hogashi Jan 4, 2023
ae017cd
options setter
hogashi Jan 4, 2023
59a8e51
use jest-chrome mock
hogashi Jan 4, 2023
992ebbf
export constant
hogashi Jan 4, 2023
539b6bd
WIP adding tests for options
hogashi Jan 4, 2023
52f0a47
jest with colors
hogashi Jan 6, 2023
48af022
jest with colors only in github actions
hogashi Jan 6, 2023
01638f6
remove window prefix
hogashi Jan 6, 2023
055021f
do not change window.chrome when chrome extension test
hogashi Jan 6, 2023
30b7e14
no window prefix
hogashi Jan 8, 2023
d3b3577
mock chrome.runtime.id
hogashi Jan 8, 2023
09ebaf2
fix syntax
hogashi Jan 8, 2023
3965f6a
implement storage get completely
hogashi Jan 8, 2023
87a9e5c
another describe
hogashi Jan 8, 2023
71f3726
add migrate localStorage to chrome.storage test, use localStorage met…
hogashi Jan 8, 2023
c258aaf
add migrated test, fix mock implementation
hogashi Jan 8, 2023
75da80b
update coverage
hogashi Jan 8, 2023
fa98317
add TODO comment
hogashi Jan 8, 2023
b627845
add setOptions test
hogashi Jan 8, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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