From 0fcd96d4356fcf8cbb6747c114a19e6216163bed Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 01:15:04 +0900 Subject: [PATCH 01/38] rename var --- __tests__/Constants.test.js | 4 ++-- src/constants.ts | 4 ++-- src/utils.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/__tests__/Constants.test.js b/__tests__/Constants.test.js index de41865f..6fc31b78 100644 --- a/__tests__/Constants.test.js +++ b/__tests__/Constants.test.js @@ -13,7 +13,7 @@ import { isFalse, OPTION_KEYS, OPTIONS_TEXT, - userjsOptions, + initialOptions, isTwitter, isTweetdeck, isImageTab, @@ -49,7 +49,7 @@ describe('定数', () => { }); it('userjs用の設定項目の初期値は全部真', () => { - expect(userjsOptions).toStrictEqual({ + expect(initialOptions).toStrictEqual({ // 公式Web SHOW_ON_TIMELINE: 'istrue', SHOW_ON_TWEET_DETAIL: 'istrue', diff --git a/src/constants.ts b/src/constants.ts index 5c1dbaf1..a211ad52 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -12,10 +12,10 @@ export interface Options { export type OptionsMaybe = { [key in keyof Options]?: TooiBoolean }; /** - * userjs 用の設定項目 + * 設定項目 * 'isfalse' とすると、その設定がオフになる */ -export const userjsOptions: Options = { +export const initialOptions: Options = { // 公式Web SHOW_ON_TIMELINE: 'istrue', SHOW_ON_TWEET_DETAIL: 'istrue', diff --git a/src/utils.ts b/src/utils.ts index ab15fc12..f60f3105 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,7 +10,7 @@ import { OPTION_KEYS, OPTION_UPDATED, STRIP_IMAGE_SUFFIX, - userjsOptions, + initialOptions, } from './constants'; /** chrome.runtime.sendMessage で送るメッセージ */ @@ -182,7 +182,7 @@ export const updateOptions = (): Promise => { // これ自体はChrome拡張機能でない(UserScriptとして読み込まれている)とき // 設定は変わりようがないので何もしない if (!isNativeChromeExtension()) { - return Promise.resolve(userjsOptions); + return Promise.resolve(initialOptions); } return new Promise((resolve) => { const request: MessageRequest = { From db978fb854790765569c4ed7dc9b0c4a68d5ee8b Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 01:25:15 +0900 Subject: [PATCH 02/38] options getter --- src/options.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/options.ts diff --git a/src/options.ts b/src/options.ts new file mode 100644 index 00000000..2fa5407f --- /dev/null +++ b/src/options.ts @@ -0,0 +1,15 @@ +import { initialOptions, OPTION_KEYS, Options } from './constants'; +import { updateOptions } from './utils'; + +export const getOptions = (): Promise => { + return new Promise((resolve) => + // chrome.storageから取ってきつつ, + // ない値はlocalStorageあるいは初期値で埋める + chrome.storage.sync.get(OPTION_KEYS, (got) => { + updateOptions().then((localStorageOptions) => { + const newOptions = { ...initialOptions, ...localStorageOptions, ...got }; + resolve(newOptions); + }); + }), + ); +}; From 0b3de9f80aa17bfa486bf9350e9a235b61b75574 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 01:40:28 +0900 Subject: [PATCH 03/38] add jest-chrome, add jest config --- jest.config.js | 4 ++++ jest.setup.js | 1 + package.json | 8 +------- yarn.lock | 15 +++++++++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 jest.config.js create mode 100644 jest.setup.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..77067c71 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + coverageReporters: ['json-summary', 'lcov', 'text'], + setupFilesAfterEnv: ['./jest.setup.js'], +}; diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 00000000..4e6c20ce --- /dev/null +++ b/jest.setup.js @@ -0,0 +1 @@ +Object.assign(global, require('jest-chrome')) \ No newline at end of file diff --git a/package.json b/package.json index 76b76fca..1b94fd42 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.7", "jest": "29.3.1", + "jest-chrome": "^0.8.0", "jest-environment-jsdom": "^29.3.1", "make-coverage-badge": "1.2.0", "npm-run-all": "4.1.5", @@ -54,12 +55,5 @@ "react": "17.0.2", "react-dom": "17.0.2", "rome": "^11.0.0" - }, - "jest": { - "coverageReporters": [ - "json-summary", - "lcov", - "text" - ] } } diff --git a/yarn.lock b/yarn.lock index 81673508..ca5d58c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1485,6 +1485,14 @@ "@types/filesystem" "*" "@types/har-format" "*" +"@types/chrome@^0.0.114": + version "0.0.114" + resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.114.tgz#8ceb33fa261f4b9e307fa7344ba8182d8d410d4e" + integrity sha512-i7qRr74IrxHtbnrZSKUuP5Uvd5EOKwlwJq/yp7+yTPihOXnPhNQO4Z5bqb1XTnrjdbUKEJicaVVbhcgtRijmLA== + dependencies: + "@types/filesystem" "*" + "@types/har-format" "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -3463,6 +3471,13 @@ jest-changed-files@^29.2.0: execa "^5.0.0" p-limit "^3.1.0" +jest-chrome@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/jest-chrome/-/jest-chrome-0.8.0.tgz#f741f5cf49292326eb9a2507111b9a77a01a495d" + integrity sha512-39RR1GT9nI4e4jsuH1vIf4l5ApxxkcstjGJr+GsOURL8f4Db0UlbRnsZaM+ZRniaGtokqklUH5VFKGZZ6YztUg== + dependencies: + "@types/chrome" "^0.0.114" + jest-circus@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a" From 06dddfbce442119708c78237e69e7a6a1da84cdd Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 01:40:43 +0900 Subject: [PATCH 04/38] add options test --- __tests__/options.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 __tests__/options.test.js diff --git a/__tests__/options.test.js b/__tests__/options.test.js new file mode 100644 index 00000000..31b98c5d --- /dev/null +++ b/__tests__/options.test.js @@ -0,0 +1,18 @@ +import { initialOptions, SHOW_ON_TIMELINE, isFalse } from '../src/constants'; +import { getOptions } from '../src/options'; + +let storageOptions = {}; +chrome.storage.sync.get = jest.fn(() => storageOptions); + +describe('設定とってくるヘルパ', () => { + it('最初は初期値', () => { + expect(getOptions()).resolves.toMatchObject(initialOptions); + }); + it('設定入れるとそれが入ってる', () => { + storageOptions = { [SHOW_ON_TIMELINE]: isFalse }; + expect(getOptions()).resolves.toMatchObject({ + ...initialOptions, + [SHOW_ON_TIMELINE]: isFalse, + }); + }); +}); From 4685c531eaf4e5ab3142dcee9c04a5636a21832c Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 01:58:58 +0900 Subject: [PATCH 05/38] use boolean version --- __tests__/options.test.js | 10 +++++----- src/constants.ts | 25 +++++++++++++++++++++++++ src/options.ts | 10 +++++++--- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index 31b98c5d..00cb3b79 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -1,4 +1,4 @@ -import { initialOptions, SHOW_ON_TIMELINE, isFalse } from '../src/constants'; +import { initialOptionsBool, SHOW_ON_TIMELINE } from '../src/constants'; import { getOptions } from '../src/options'; let storageOptions = {}; @@ -6,13 +6,13 @@ chrome.storage.sync.get = jest.fn(() => storageOptions); describe('設定とってくるヘルパ', () => { it('最初は初期値', () => { - expect(getOptions()).resolves.toMatchObject(initialOptions); + expect(getOptions()).resolves.toMatchObject(initialOptionsBool); }); it('設定入れるとそれが入ってる', () => { - storageOptions = { [SHOW_ON_TIMELINE]: isFalse }; + storageOptions = { [SHOW_ON_TIMELINE]: false }; expect(getOptions()).resolves.toMatchObject({ - ...initialOptions, - [SHOW_ON_TIMELINE]: isFalse, + ...initialOptionsBool, + [SHOW_ON_TIMELINE]: false, }); }); }); diff --git a/src/constants.ts b/src/constants.ts index a211ad52..a60a1fd4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,14 @@ +export interface OptionsBool { + // 公式Web + SHOW_ON_TIMELINE: boolean; + SHOW_ON_TWEET_DETAIL: boolean; + // TweetDeck + SHOW_ON_TWEETDECK_TIMELINE: boolean; + SHOW_ON_TWEETDECK_TWEET_DETAIL: boolean; + // 画像ページ + STRIP_IMAGE_SUFFIX: boolean; +} + export interface Options { // 公式Web SHOW_ON_TIMELINE: TooiBoolean; @@ -11,6 +22,20 @@ export interface Options { export type OptionsMaybe = { [key in keyof Options]?: TooiBoolean }; +/** + * 設定項目 + */ +export const initialOptionsBool: OptionsBool = { + // 公式Web + SHOW_ON_TIMELINE: true, + SHOW_ON_TWEET_DETAIL: true, + // TweetDeck + SHOW_ON_TWEETDECK_TIMELINE: true, + SHOW_ON_TWEETDECK_TWEET_DETAIL: true, + // 画像ページ + STRIP_IMAGE_SUFFIX: true, +}; + /** * 設定項目 * 'isfalse' とすると、その設定がオフになる diff --git a/src/options.ts b/src/options.ts index 2fa5407f..8c0d991c 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,14 +1,18 @@ -import { initialOptions, OPTION_KEYS, Options } from './constants'; +import { initialOptions, OPTION_KEYS, isTrue, OptionsBool, initialOptionsBool } from './constants'; import { updateOptions } from './utils'; -export const getOptions = (): Promise => { +export const getOptions = (): Promise => { return new Promise((resolve) => // chrome.storageから取ってきつつ, // ない値はlocalStorageあるいは初期値で埋める chrome.storage.sync.get(OPTION_KEYS, (got) => { updateOptions().then((localStorageOptions) => { const newOptions = { ...initialOptions, ...localStorageOptions, ...got }; - resolve(newOptions); + const newOptionsBool = { ...initialOptionsBool }; + OPTION_KEYS.forEach((key) => { + newOptionsBool[key] = newOptions[key] === isTrue; + }); + resolve(newOptionsBool); }); }), ); From 22e4867ef86fcff31a82d78857459d5d09617cc5 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:00:48 +0900 Subject: [PATCH 06/38] call callback in mock --- __tests__/options.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index 00cb3b79..7fc5c927 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -2,7 +2,7 @@ import { initialOptionsBool, SHOW_ON_TIMELINE } from '../src/constants'; import { getOptions } from '../src/options'; let storageOptions = {}; -chrome.storage.sync.get = jest.fn(() => storageOptions); +chrome.storage.sync.get = jest.fn((_keys, callback) => callback(storageOptions)); describe('設定とってくるヘルパ', () => { it('最初は初期値', () => { From 1b41b1098250519adfee5792c25546b50bb960b3 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:02:22 +0900 Subject: [PATCH 07/38] suppose that chrome.storage holds boolean --- src/options.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/options.ts b/src/options.ts index 8c0d991c..a1c22f50 100644 --- a/src/options.ts +++ b/src/options.ts @@ -7,12 +7,13 @@ export const getOptions = (): Promise => { // ない値はlocalStorageあるいは初期値で埋める chrome.storage.sync.get(OPTION_KEYS, (got) => { updateOptions().then((localStorageOptions) => { - const newOptions = { ...initialOptions, ...localStorageOptions, ...got }; + const newOptions = { ...initialOptions, ...localStorageOptions }; + // 真偽値にして返す const newOptionsBool = { ...initialOptionsBool }; OPTION_KEYS.forEach((key) => { newOptionsBool[key] = newOptions[key] === isTrue; }); - resolve(newOptionsBool); + resolve({ ...newOptionsBool, ...got }); }); }), ); From 1ee0e02a98ef09d6903d4964745d2b2ab540a3e8 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:05:51 +0900 Subject: [PATCH 08/38] use var --- src/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index f60f3105..8d9d00a7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,6 +11,7 @@ import { OPTION_UPDATED, STRIP_IMAGE_SUFFIX, initialOptions, + isFalse, } from './constants'; /** chrome.runtime.sendMessage で送るメッセージ */ @@ -283,7 +284,7 @@ export const fixFileNameOnSaveCommand = (options: Options): void => { // キーを押したとき document.addEventListener('keydown', (e) => { // 設定が有効なら - if (options[STRIP_IMAGE_SUFFIX] !== 'isfalse') { + if (options[STRIP_IMAGE_SUFFIX] !== isFalse) { downloadImage(e); } }); From 7e14df5fe8c6127872fe73d6953505eafa2ec1d4 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:12:47 +0900 Subject: [PATCH 09/38] use boolean options --- src/ButtonSetter.ts | 28 +++++++++++----------------- src/ButtonSetterTweetDeck.ts | 14 +++++--------- src/main.ts | 5 +++-- src/utils.ts | 14 ++++++++------ 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/src/ButtonSetter.ts b/src/ButtonSetter.ts index e11b2c86..474ecdca 100644 --- a/src/ButtonSetter.ts +++ b/src/ButtonSetter.ts @@ -1,9 +1,9 @@ -import { isFalse, Options, SHOW_ON_TIMELINE, SHOW_ON_TWEET_DETAIL } from './constants'; +import { OptionsBool, SHOW_ON_TIMELINE, SHOW_ON_TWEET_DETAIL } from './constants'; import { onOriginalButtonClick, printException, setStyle } from './utils'; export interface ButtonSetterType { - setButtonOnTimeline: (currentOptions: Options) => void; - setButtonOnTweetDetail: (currentOptions: Options) => void; + setButtonOnTimeline: (currentOptions: OptionsBool) => void; + setButtonOnTweetDetail: (currentOptions: OptionsBool) => void; } /** @@ -11,7 +11,7 @@ export interface ButtonSetterType { */ export class ButtonSetter implements ButtonSetterType { // タイムラインにボタン表示 - public setButtonOnTimeline(currentOptions: Options): void { + public setButtonOnTimeline(currentOptions: OptionsBool): void { // 昔のビューの処理はしばらく残す // ref: https://github.com/hogashi/twitterOpenOriginalImage/issues/32#issuecomment-578510155 if (document.querySelector('#react-root')) { @@ -22,7 +22,7 @@ export class ButtonSetter implements ButtonSetterType { } // ツイート詳細にボタン表示 - public setButtonOnTweetDetail(currentOptions: Options): void { + public setButtonOnTweetDetail(currentOptions: OptionsBool): void { // 昔のビューの処理はしばらく残す // TODO: Reactレイアウトでも実装する必要がある? // ref: https://github.com/hogashi/twitterOpenOriginalImage/issues/32#issuecomment-578510155 @@ -116,11 +116,9 @@ export class ButtonSetter implements ButtonSetterType { container.appendChild(button); } - private _setButtonOnTimeline(currentOptions: Options): void { + private _setButtonOnTimeline(currentOptions: OptionsBool): void { // タイムラインにボタン表示する設定がされているときだけ実行する - // - isTrue か 設定なし のとき ON - // - isFalse のとき OFF - if (!(currentOptions[SHOW_ON_TIMELINE] !== isFalse)) { + if (!currentOptions[SHOW_ON_TIMELINE]) { return; } const tweets = document.getElementsByClassName('js-stream-tweet'); @@ -161,11 +159,9 @@ export class ButtonSetter implements ButtonSetterType { }); } - private _setButtonOnTweetDetail(currentOptions: Options): void { + private _setButtonOnTweetDetail(currentOptions: OptionsBool): void { // ツイート詳細にボタン表示する設定がされているときだけ実行する - // - isTrue か 設定なし のとき ON - // - isFalse のとき OFF - if (!(currentOptions[SHOW_ON_TWEET_DETAIL] !== isFalse)) { + if (!currentOptions[SHOW_ON_TWEET_DETAIL]) { return; } const className = 'tooi-button-container-detail'; @@ -201,11 +197,9 @@ export class ButtonSetter implements ButtonSetterType { }); } - private _setButtonOnReactLayoutTimeline(currentOptions: Options): void { + private _setButtonOnReactLayoutTimeline(currentOptions: OptionsBool): void { // ツイート詳細にボタン表示する設定がされているときだけ実行する - // - isTrue か 設定なし のとき ON - // - isFalse のとき OFF - if (!(currentOptions[SHOW_ON_TIMELINE] !== isFalse)) { + if (!currentOptions[SHOW_ON_TIMELINE]) { return; } const className = 'tooi-button-container-react-timeline'; diff --git a/src/ButtonSetterTweetDeck.ts b/src/ButtonSetterTweetDeck.ts index 4ef6c1b4..201c744c 100644 --- a/src/ButtonSetterTweetDeck.ts +++ b/src/ButtonSetterTweetDeck.ts @@ -1,5 +1,5 @@ import { ButtonSetterType } from './ButtonSetter'; -import { SHOW_ON_TWEETDECK_TIMELINE, isFalse, SHOW_ON_TWEETDECK_TWEET_DETAIL, Options } from './constants'; +import { SHOW_ON_TWEETDECK_TIMELINE, SHOW_ON_TWEETDECK_TWEET_DETAIL, OptionsBool } from './constants'; import { printException, setStyle, onOriginalButtonClick } from './utils'; /** @@ -7,11 +7,9 @@ import { printException, setStyle, onOriginalButtonClick } from './utils'; */ export class ButtonSetterTweetDeck implements ButtonSetterType { // タイムラインにボタン表示 - public setButtonOnTimeline(currentOptions: Options): void { + public setButtonOnTimeline(currentOptions: OptionsBool): void { // タイムラインにボタン表示する設定がされているときだけ実行する - // - isTrue か 設定なし のとき ON - // - isFalse のとき OFF - if (!(currentOptions[SHOW_ON_TWEETDECK_TIMELINE] !== isFalse)) { + if (!currentOptions[SHOW_ON_TWEETDECK_TIMELINE]) { return; } // if タイムラインのツイートを取得できたら @@ -62,11 +60,9 @@ export class ButtonSetterTweetDeck implements ButtonSetterType { } // ツイート詳細にボタン表示 - public setButtonOnTweetDetail(currentOptions: Options): void { + public setButtonOnTweetDetail(currentOptions: OptionsBool): void { // ツイート詳細にボタン表示する設定がされているときだけ実行する - // - isTrue か 設定なし のとき ON - // - isFalse のとき OFF - if (!(currentOptions[SHOW_ON_TWEETDECK_TWEET_DETAIL] !== isFalse)) { + if (!currentOptions[SHOW_ON_TWEETDECK_TWEET_DETAIL]) { return; } // if ツイート詳細を取得できたら diff --git a/src/main.ts b/src/main.ts index 90c78cfe..83cc0f04 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,8 @@ import { isImageTab, isTweetdeck, isTwitter } from './constants'; -import { fixFileNameOnSaveCommand, setOriginalButton, updateOptions } from './utils'; +import { fixFileNameOnSaveCommand, setOriginalButton } from './utils'; +import { getOptions } from './options'; -updateOptions().then((options) => { +getOptions().then((options) => { if (isTwitter() || isTweetdeck()) { /** 公式Web/TweetDeck */ setOriginalButton(options); diff --git a/src/utils.ts b/src/utils.ts index 8d9d00a7..dbd71447 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -12,7 +12,9 @@ import { STRIP_IMAGE_SUFFIX, initialOptions, isFalse, + OptionsBool, } from './constants'; +import { getOptions } from './options'; /** chrome.runtime.sendMessage で送るメッセージ */ export interface MessageRequest { @@ -208,7 +210,7 @@ export const updateOptions = (): Promise => { }; /** Originalボタンおく */ -export const setOriginalButton = (options: Options): void => { +export const setOriginalButton = (options: OptionsBool): void => { // 実行の間隔(ms) const INTERVAL = 300; @@ -216,7 +218,7 @@ export const setOriginalButton = (options: Options): void => { const buttonSetter = getButtonSetter(); // ボタンを設置 - const setButton = (currentOptions: Options): void => { + const setButton = (currentOptions: OptionsBool): void => { // console.log('setButton: ' + currentOptions['SHOW_ON_TIMELINE'] + ' ' + currentOptions['SHOW_ON_TWEET_DETAIL']) // debug buttonSetter.setButtonOnTimeline(currentOptions); buttonSetter.setButtonOnTweetDetail(currentOptions); @@ -224,7 +226,7 @@ export const setOriginalButton = (options: Options): void => { let isInterval = false; let deferred = false; - const setButtonWithInterval = (currentOptions: Options): void => { + const setButtonWithInterval = (currentOptions: OptionsBool): void => { // 短時間に何回も実行しないようインターバルを設ける if (isInterval) { deferred = true; @@ -263,7 +265,7 @@ export const setOriginalButton = (options: Options): void => { console.log(window.chrome.runtime.lastError); } if (request.method === OPTION_UPDATED) { - updateOptions().then((options) => { + getOptions().then((options) => { // ボタンを(再)設置 setButtonWithInterval(options); sendResponse({ data: 'done' }); @@ -280,11 +282,11 @@ export const setOriginalButton = (options: Options): void => { * twitterの画像を表示したときのC-sを拡張 * 画像のファイル名を「~.jpg-orig」「~.png-orig」ではなく「~-orig.jpg」「~-orig.png」にする */ -export const fixFileNameOnSaveCommand = (options: Options): void => { +export const fixFileNameOnSaveCommand = (options: OptionsBool): void => { // キーを押したとき document.addEventListener('keydown', (e) => { // 設定が有効なら - if (options[STRIP_IMAGE_SUFFIX] !== isFalse) { + if (options[STRIP_IMAGE_SUFFIX]) { downloadImage(e); } }); From 659845517b04f2834e7492c5a0c247461c2b047e Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:16:20 +0900 Subject: [PATCH 10/38] use var in tests --- __tests__/ButtonSetter.test.js | 12 ++++++------ __tests__/ButtonSetterTweetDeck.test.js | 12 ++++++------ __tests__/Constants.test.js | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/__tests__/ButtonSetter.test.js b/__tests__/ButtonSetter.test.js index 0f41ec77..b63677e2 100644 --- a/__tests__/ButtonSetter.test.js +++ b/__tests__/ButtonSetter.test.js @@ -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, }; } diff --git a/__tests__/ButtonSetterTweetDeck.test.js b/__tests__/ButtonSetterTweetDeck.test.js index 8097e6e6..cc2efcaf 100644 --- a/__tests__/ButtonSetterTweetDeck.test.js +++ b/__tests__/ButtonSetterTweetDeck.test.js @@ -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, }; } diff --git a/__tests__/Constants.test.js b/__tests__/Constants.test.js index 6fc31b78..9e6d947d 100644 --- a/__tests__/Constants.test.js +++ b/__tests__/Constants.test.js @@ -51,13 +51,13 @@ describe('定数', () => { it('userjs用の設定項目の初期値は全部真', () => { 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([ From 5a4c3419989fa57febb52165be9541f676c5b9dc Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:41:27 +0900 Subject: [PATCH 11/38] chrome.storage must be used in background --- __tests__/options.test.js | 18 ------------------ dist/manifest.json | 2 +- src/background.ts | 18 ++++++++++++++---- src/main.ts | 5 ++--- src/options.ts | 20 -------------------- src/utils.ts | 30 ++++++++++++------------------ 6 files changed, 29 insertions(+), 64 deletions(-) delete mode 100644 __tests__/options.test.js delete mode 100644 src/options.ts diff --git a/__tests__/options.test.js b/__tests__/options.test.js deleted file mode 100644 index 7fc5c927..00000000 --- a/__tests__/options.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import { initialOptionsBool, SHOW_ON_TIMELINE } from '../src/constants'; -import { getOptions } from '../src/options'; - -let storageOptions = {}; -chrome.storage.sync.get = jest.fn((_keys, callback) => callback(storageOptions)); - -describe('設定とってくるヘルパ', () => { - it('最初は初期値', () => { - expect(getOptions()).resolves.toMatchObject(initialOptionsBool); - }); - it('設定入れるとそれが入ってる', () => { - storageOptions = { [SHOW_ON_TIMELINE]: false }; - expect(getOptions()).resolves.toMatchObject({ - ...initialOptionsBool, - [SHOW_ON_TIMELINE]: false, - }); - }); -}); diff --git a/dist/manifest.json b/dist/manifest.json index 497d889e..01c4e1f8 100644 --- a/dist/manifest.json +++ b/dist/manifest.json @@ -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", diff --git a/src/background.ts b/src/background.ts index 1155ff29..13ba01de 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,13 +1,23 @@ -import { GET_LOCAL_STORAGE } from './constants'; -import { MessageRequest, MessageResponse } from './utils'; +import { GET_LOCAL_STORAGE, initialOptions, initialOptionsBool, isTrue, OptionsBool, OPTION_KEYS } from './constants'; +import { MessageRequest, MessageResponseBool } from './utils'; // バックグラウンドで実行される window.chrome.runtime.onMessage.addListener( - (request: MessageRequest, _, sendResponse: (res: MessageResponse) => void) => { + (request: MessageRequest, _, sendResponse: (res: MessageResponseBool) => void) => { // console.log(chrome.runtime.lastError); if (request.method === GET_LOCAL_STORAGE) { - sendResponse({ data: localStorage }); + // chrome.storageから取ってきつつ, + // ない値はlocalStorageあるいは初期値で埋める + chrome.storage.sync.get(OPTION_KEYS, (got) => { + const newOptions = { ...initialOptions, ...localStorage }; + // 真偽値にして返す + const newOptionsBool = { ...initialOptionsBool }; + OPTION_KEYS.forEach((key) => { + newOptionsBool[key] = newOptions[key] === isTrue; + }); + sendResponse({ data: { ...newOptionsBool, ...got } }); + }); } else { sendResponse({ data: null }); } diff --git a/src/main.ts b/src/main.ts index 83cc0f04..90c78cfe 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,7 @@ import { isImageTab, isTweetdeck, isTwitter } from './constants'; -import { fixFileNameOnSaveCommand, setOriginalButton } from './utils'; -import { getOptions } from './options'; +import { fixFileNameOnSaveCommand, setOriginalButton, updateOptions } from './utils'; -getOptions().then((options) => { +updateOptions().then((options) => { if (isTwitter() || isTweetdeck()) { /** 公式Web/TweetDeck */ setOriginalButton(options); diff --git a/src/options.ts b/src/options.ts deleted file mode 100644 index a1c22f50..00000000 --- a/src/options.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { initialOptions, OPTION_KEYS, isTrue, OptionsBool, initialOptionsBool } from './constants'; -import { updateOptions } from './utils'; - -export const getOptions = (): Promise => { - return new Promise((resolve) => - // chrome.storageから取ってきつつ, - // ない値はlocalStorageあるいは初期値で埋める - chrome.storage.sync.get(OPTION_KEYS, (got) => { - updateOptions().then((localStorageOptions) => { - const newOptions = { ...initialOptions, ...localStorageOptions }; - // 真偽値にして返す - const newOptionsBool = { ...initialOptionsBool }; - OPTION_KEYS.forEach((key) => { - newOptionsBool[key] = newOptions[key] === isTrue; - }); - resolve({ ...newOptionsBool, ...got }); - }); - }), - ); -}; diff --git a/src/utils.ts b/src/utils.ts index dbd71447..42a2adbe 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -13,13 +13,17 @@ import { initialOptions, isFalse, OptionsBool, + initialOptionsBool, } from './constants'; -import { getOptions } from './options'; /** chrome.runtime.sendMessage で送るメッセージ */ export interface MessageRequest { method: string; } +/** chrome.runtime.sendMessage で返るメッセージ(真偽値版) */ +export interface MessageResponseBool { + data: OptionsBool | null; +} /** chrome.runtime.sendMessage で返るメッセージ */ export interface MessageResponse { data: { [key: string]: string } | null; @@ -179,33 +183,23 @@ export const getButtonSetter = (): ButtonSetterType => /** * 設定項目更新 - * background script に問い合わせて返ってきた値で options をつくって返す + * background script に問い合わせて返ってきた値で options (真偽値) をつくって返す */ -export const updateOptions = (): Promise => { +export const updateOptions = (): Promise => { // これ自体はChrome拡張機能でない(UserScriptとして読み込まれている)とき // 設定は変わりようがないので何もしない if (!isNativeChromeExtension()) { - return Promise.resolve(initialOptions); + return Promise.resolve(initialOptionsBool); } - return new Promise((resolve) => { + return new Promise((resolve) => { const request: MessageRequest = { method: GET_LOCAL_STORAGE, }; - const callback = (response: MessageResponse): void => { + const callback = (response: MessageResponseBool): void => { // 何かおかしくて設定内容取ってこれなかったらデフォルトということにする - resolve(response?.data ? response.data : {}); + resolve(response?.data ? response.data : initialOptionsBool); }; window.chrome.runtime.sendMessage(request, callback); - }).then((data: OptionsMaybe) => { - const newOptions: OptionsMaybe = {}; - // ここで全部埋めるので newOptions は Options になる - OPTION_KEYS.forEach((key) => { - newOptions[key] = data[key] || isTrue; - }); - - // console.log('get options (then): ', newOptions); // debug - - return newOptions as Options; }); }; @@ -265,7 +259,7 @@ export const setOriginalButton = (options: OptionsBool): void => { console.log(window.chrome.runtime.lastError); } if (request.method === OPTION_UPDATED) { - getOptions().then((options) => { + updateOptions().then((options) => { // ボタンを(再)設置 setButtonWithInterval(options); sendResponse({ data: 'done' }); From b3aefb3e3ee58e56c71f1c9138c604b1cbd0de6d Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 02:45:47 +0900 Subject: [PATCH 12/38] add TODO --- src/background.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/background.ts b/src/background.ts index 13ba01de..e759de36 100644 --- a/src/background.ts +++ b/src/background.ts @@ -9,6 +9,7 @@ window.chrome.runtime.onMessage.addListener( if (request.method === GET_LOCAL_STORAGE) { // chrome.storageから取ってきつつ, // ない値はlocalStorageあるいは初期値で埋める + // TODO: むずい感じになってきたので, テストできるようにlocalStorageとchrome.storageだけ返すことにしたい chrome.storage.sync.get(OPTION_KEYS, (got) => { const newOptions = { ...initialOptions, ...localStorage }; // 真偽値にして返す From 463a7ca330f74d5384f219406eaa8afa79acc8cd Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 23:31:46 +0900 Subject: [PATCH 13/38] migrate options to chrome storage when get --- src/background.ts | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/background.ts b/src/background.ts index e759de36..4728aa70 100644 --- a/src/background.ts +++ b/src/background.ts @@ -3,21 +3,38 @@ import { MessageRequest, MessageResponseBool } from './utils'; // バックグラウンドで実行される +const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; + window.chrome.runtime.onMessage.addListener( (request: MessageRequest, _, sendResponse: (res: MessageResponseBool) => void) => { // console.log(chrome.runtime.lastError); if (request.method === GET_LOCAL_STORAGE) { // chrome.storageから取ってきつつ, - // ない値はlocalStorageあるいは初期値で埋める - // TODO: むずい感じになってきたので, テストできるようにlocalStorageとchrome.storageだけ返すことにしたい - chrome.storage.sync.get(OPTION_KEYS, (got) => { - const newOptions = { ...initialOptions, ...localStorage }; - // 真偽値にして返す - const newOptionsBool = { ...initialOptionsBool }; - OPTION_KEYS.forEach((key) => { - newOptionsBool[key] = newOptions[key] === isTrue; - }); - sendResponse({ data: { ...newOptionsBool, ...got } }); + // まだ移行してないときはlocalStorageあるいは初期値を移行 + chrome.storage.sync.get(MIGRATED_TO_CHROME_STORAGE, (isMigrated) => { + if (!isMigrated[MIGRATED_TO_CHROME_STORAGE]) { + const newOptions = { ...initialOptions, ...localStorage }; + // 真偽値にして移行する + const newOptionsBool = { ...initialOptionsBool }; + OPTION_KEYS.forEach((key) => { + newOptionsBool[key] = newOptions[key] === isTrue; + }); + chrome.storage.sync.set( + { + ...newOptionsBool, + [MIGRATED_TO_CHROME_STORAGE]: true, + }, + () => { + // 移行できたら新しい値を返す + sendResponse({ data: newOptionsBool }); + }, + ); + } else { + chrome.storage.sync.get(OPTION_KEYS, (got) => { + // 初期値をフォールバックとしておく + sendResponse({ data: { ...initialOptionsBool, ...got } }); + }); + } }); } else { sendResponse({ data: null }); From afe32ae7e7af4b65f81bfe9adebe03a54c958f0a Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 23:43:46 +0900 Subject: [PATCH 14/38] split out options getter --- src/background.ts | 33 ++++----------------------------- src/options.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 src/options.ts diff --git a/src/background.ts b/src/background.ts index 4728aa70..31914128 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,40 +1,15 @@ -import { GET_LOCAL_STORAGE, initialOptions, initialOptionsBool, isTrue, OptionsBool, OPTION_KEYS } from './constants'; +import { GET_LOCAL_STORAGE } from './constants'; +import { getOptions } from './options'; import { MessageRequest, MessageResponseBool } from './utils'; // バックグラウンドで実行される -const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; - window.chrome.runtime.onMessage.addListener( (request: MessageRequest, _, sendResponse: (res: MessageResponseBool) => void) => { // console.log(chrome.runtime.lastError); if (request.method === GET_LOCAL_STORAGE) { - // chrome.storageから取ってきつつ, - // まだ移行してないときはlocalStorageあるいは初期値を移行 - chrome.storage.sync.get(MIGRATED_TO_CHROME_STORAGE, (isMigrated) => { - if (!isMigrated[MIGRATED_TO_CHROME_STORAGE]) { - const newOptions = { ...initialOptions, ...localStorage }; - // 真偽値にして移行する - const newOptionsBool = { ...initialOptionsBool }; - OPTION_KEYS.forEach((key) => { - newOptionsBool[key] = newOptions[key] === isTrue; - }); - chrome.storage.sync.set( - { - ...newOptionsBool, - [MIGRATED_TO_CHROME_STORAGE]: true, - }, - () => { - // 移行できたら新しい値を返す - sendResponse({ data: newOptionsBool }); - }, - ); - } else { - chrome.storage.sync.get(OPTION_KEYS, (got) => { - // 初期値をフォールバックとしておく - sendResponse({ data: { ...initialOptionsBool, ...got } }); - }); - } + getOptions().then((options) => { + sendResponse({ data: options }); }); } else { sendResponse({ data: null }); diff --git a/src/options.ts b/src/options.ts new file mode 100644 index 00000000..1e3617ce --- /dev/null +++ b/src/options.ts @@ -0,0 +1,35 @@ +import { OptionsBool, initialOptions, initialOptionsBool, OPTION_KEYS, isTrue } from './constants'; + +const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; + +export const getOptions = (): Promise => { + return new Promise((resolve) => { + // chrome.storageから取ってきつつ, + // まだ移行してないときはlocalStorageあるいは初期値を移行 + chrome.storage.sync.get(MIGRATED_TO_CHROME_STORAGE, (isMigrated) => { + if (!isMigrated[MIGRATED_TO_CHROME_STORAGE]) { + const newOptions = { ...initialOptions, ...localStorage }; + // 真偽値にして移行する + const newOptionsBool = { ...initialOptionsBool }; + OPTION_KEYS.forEach((key) => { + newOptionsBool[key] = newOptions[key] === isTrue; + }); + chrome.storage.sync.set( + { + ...newOptionsBool, + [MIGRATED_TO_CHROME_STORAGE]: true, + }, + () => { + // 移行できたら新しい値を返す + resolve(newOptionsBool); + }, + ); + } else { + chrome.storage.sync.get(OPTION_KEYS, (got) => { + // 初期値をフォールバックとしておく + resolve({ ...initialOptionsBool, ...got }); + }); + } + }); + }); +}; From eeb848d46543dfc89f4bc4460548043aba5605ff Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 23:43:53 +0900 Subject: [PATCH 15/38] use getter --- src/popup.tsx | 53 +++++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/src/popup.tsx b/src/popup.tsx index 0a407a62..7072eff3 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -11,8 +11,6 @@ import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import { OPTION_KEYS, - isTrue, - isFalse, HOST_TWITTER_COM, HOST_MOBILE_TWITTER_COM, HOST_TWEETDECK_TWITTER_COM, @@ -24,8 +22,10 @@ import { SHOW_ON_TWEETDECK_TWEET_DETAIL, STRIP_IMAGE_SUFFIX, OPTIONS_TEXT, + OptionsBool, } from './constants'; import { printException } from './utils'; +import { getOptions } from './options'; /* popup.js */ // ツールバー右に表示される拡張機能のボタンをクリック、または @@ -34,7 +34,7 @@ import { printException } from './utils'; interface Props { optionsText: { [key: string]: string }; optionKeys: typeof OPTION_KEYS; - optionsEnabled: { [key: string]: boolean }; + optionsEnabled: OptionsBool; } export const Popup = (props: Props): JSX.Element => { @@ -42,9 +42,7 @@ export const Popup = (props: Props): JSX.Element => { const [enabled, setEnabled] = useState(optionsEnabled); const onSave = useCallback(() => { - optionKeys.forEach((key) => { - localStorage[key] = enabled[key] ? isTrue : isFalse; - }); + chrome.storage.sync.set(enabled); window.chrome.tabs.query({}, (result) => result.forEach((tab) => { // console.log(tab); @@ -139,30 +137,23 @@ export const Popup = (props: Props): JSX.Element => { ); }; -const optionsText = OPTIONS_TEXT; -const optionKeys = OPTION_KEYS; -const optionsEnabled: { [key: string]: boolean } = {}; -optionKeys.forEach((key) => { - // 最初はどっちも機能オンであってほしい - // 最初は値が入っていないので、「if isfalseでないなら機能オン」とする - optionsEnabled[key] = localStorage[key] !== isFalse; -}); +getOptions().then(optionsEnabled => { + const props = { + optionsText: OPTIONS_TEXT, + optionKeys: OPTION_KEYS, + optionsEnabled, + }; -const props = { - optionsText, - optionKeys, - optionsEnabled, -}; - -let root = document.getElementById('root'); -if (!root) { - root = document.createElement('div'); - root.id = 'root'; - const body = document.querySelector('body'); - if (body) { - body.appendChild(root); - } else { - printException('cant find body'); + let root = document.getElementById('root'); + if (!root) { + root = document.createElement('div'); + root.id = 'root'; + const body = document.querySelector('body'); + if (body) { + body.appendChild(root); + } else { + printException('cant find body'); + } } -} -ReactDOM.render(, document.getElementById('root')); + ReactDOM.render(, document.getElementById('root')); + }); From 1eaf9be8b001630de0dde15df4fc24197eb7d8e5 Mon Sep 17 00:00:00 2001 From: hogashi Date: Wed, 4 Jan 2023 23:51:31 +0900 Subject: [PATCH 16/38] newline at EOF --- jest.setup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.setup.js b/jest.setup.js index 4e6c20ce..4acf7987 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1 +1 @@ -Object.assign(global, require('jest-chrome')) \ No newline at end of file +Object.assign(global, require('jest-chrome')); From 47af79e38227bf27d28c08e373f0f9858a28245a Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 00:02:03 +0900 Subject: [PATCH 17/38] move extension contexts scripts --- __tests__/popup.test.js | 2 +- src/{ => extension-contexts}/background.ts | 4 ++-- src/{ => extension-contexts}/options.ts | 2 +- src/{ => extension-contexts}/popup.tsx | 4 ++-- webpack.config.js | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename src/{ => extension-contexts}/background.ts (80%) rename src/{ => extension-contexts}/options.ts (97%) rename src/{ => extension-contexts}/popup.tsx (98%) diff --git a/__tests__/popup.test.js b/__tests__/popup.test.js index aeceb58e..d2c51d04 100644 --- a/__tests__/popup.test.js +++ b/__tests__/popup.test.js @@ -12,7 +12,7 @@ import { isTrue, OPTION_KEYS, } from '../src/constants'; -import { Popup } from '../src/popup'; +import { Popup } from '../src/extension-contexts/popup'; describe('Popup', () => { it('render', () => { diff --git a/src/background.ts b/src/extension-contexts/background.ts similarity index 80% rename from src/background.ts rename to src/extension-contexts/background.ts index 31914128..4179c681 100644 --- a/src/background.ts +++ b/src/extension-contexts/background.ts @@ -1,6 +1,6 @@ -import { GET_LOCAL_STORAGE } from './constants'; +import { GET_LOCAL_STORAGE } from '../constants'; import { getOptions } from './options'; -import { MessageRequest, MessageResponseBool } from './utils'; +import { MessageRequest, MessageResponseBool } from '../utils'; // バックグラウンドで実行される diff --git a/src/options.ts b/src/extension-contexts/options.ts similarity index 97% rename from src/options.ts rename to src/extension-contexts/options.ts index 1e3617ce..7cb2ae92 100644 --- a/src/options.ts +++ b/src/extension-contexts/options.ts @@ -1,4 +1,4 @@ -import { OptionsBool, initialOptions, initialOptionsBool, OPTION_KEYS, isTrue } from './constants'; +import { OptionsBool, initialOptions, initialOptionsBool, OPTION_KEYS, isTrue } from '../constants'; const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; diff --git a/src/popup.tsx b/src/extension-contexts/popup.tsx similarity index 98% rename from src/popup.tsx rename to src/extension-contexts/popup.tsx index 7072eff3..360db5a0 100644 --- a/src/popup.tsx +++ b/src/extension-contexts/popup.tsx @@ -23,8 +23,8 @@ import { STRIP_IMAGE_SUFFIX, OPTIONS_TEXT, OptionsBool, -} from './constants'; -import { printException } from './utils'; +} from '../constants'; +import { printException } from '../utils'; import { getOptions } from './options'; /* popup.js */ diff --git a/webpack.config.js b/webpack.config.js index 2fa143cb..29949826 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,8 +1,8 @@ module.exports = { entry: { main: './src/main.ts', - background: './src/background.ts', - popup: './src/popup.tsx', + background: './src/extension-contexts/background.ts', + popup: './src/extension-contexts/popup.tsx', }, output: { filename: '[name].bundle.js', From a387d2a8da77ce38f817f90a6108e94efab81287 Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 00:08:59 +0900 Subject: [PATCH 18/38] fix tests for utils --- __tests__/Utils.test.js | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/__tests__/Utils.test.js b/__tests__/Utils.test.js index a0a0debe..3a455d0f 100644 --- a/__tests__/Utils.test.js +++ b/__tests__/Utils.test.js @@ -1,5 +1,5 @@ // import * as main from '../src/main'; -import { isTrue, isFalse, OPTION_KEYS } from '../src/constants'; +import { isTrue, isFalse, OPTION_KEYS, initialOptionsBool } from '../src/constants'; import { printException, collectUrlParams, @@ -254,30 +254,22 @@ describe('Utils', () => { }); 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); + window.chrome.runtime.sendMessage = jest.fn((_, 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 } })); 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); + await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool); }); }); @@ -292,11 +284,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); }); }); }); From 07d1155e359eda04eb32f4293f0d45d847b5cf90 Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 00:43:21 +0900 Subject: [PATCH 19/38] remove unused imports --- __tests__/Utils.test.js | 2 +- src/utils.ts | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/__tests__/Utils.test.js b/__tests__/Utils.test.js index 3a455d0f..787c9211 100644 --- a/__tests__/Utils.test.js +++ b/__tests__/Utils.test.js @@ -1,5 +1,5 @@ // import * as main from '../src/main'; -import { isTrue, isFalse, OPTION_KEYS, initialOptionsBool } from '../src/constants'; +import { OPTION_KEYS, initialOptionsBool } from '../src/constants'; import { printException, collectUrlParams, diff --git a/src/utils.ts b/src/utils.ts index 42a2adbe..98cf69fb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,15 +3,9 @@ import { ButtonSetterTweetDeck } from './ButtonSetterTweetDeck'; import { GET_LOCAL_STORAGE, isNativeChromeExtension, - isTrue, isTweetdeck, - Options, - OptionsMaybe, - OPTION_KEYS, OPTION_UPDATED, STRIP_IMAGE_SUFFIX, - initialOptions, - isFalse, OptionsBool, initialOptionsBool, } from './constants'; From 53fec06989cb96667ee25378fd5ae51d9cab9af9 Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 01:42:43 +0900 Subject: [PATCH 20/38] use jest-chrome mock --- __tests__/popup.test.js | 98 +++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/__tests__/popup.test.js b/__tests__/popup.test.js index d2c51d04..1fa9fa67 100644 --- a/__tests__/popup.test.js +++ b/__tests__/popup.test.js @@ -7,21 +7,23 @@ const { shallow } = Enzyme; import { OPTIONS_TEXT, SHOW_ON_TIMELINE, - isFalse, SHOW_ON_TWEETDECK_TIMELINE, - isTrue, OPTION_KEYS, + initialOptionsBool, } from '../src/constants'; 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, @@ -33,22 +35,13 @@ 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, @@ -56,35 +49,31 @@ describe('Popup', () => { 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(); it('渡した設定がそのまま保存される', () => { @@ -92,25 +81,30 @@ describe('Popup', () => { // 送りたいタブは正しい形式かつ対象ホストなタブのみ 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, + }); }); }); }); From ae017cd3c276be712d56ec1765b5723c890bddfb Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 01:43:12 +0900 Subject: [PATCH 21/38] options setter --- src/extension-contexts/options.ts | 6 ++++++ src/extension-contexts/popup.tsx | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/extension-contexts/options.ts b/src/extension-contexts/options.ts index 7cb2ae92..75d670db 100644 --- a/src/extension-contexts/options.ts +++ b/src/extension-contexts/options.ts @@ -2,6 +2,12 @@ import { OptionsBool, initialOptions, initialOptionsBool, OPTION_KEYS, isTrue } const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; +export const setOptions = (options: OptionsBool): void => { + chrome.storage.sync.set(options, () => { + console.log('options set'); + }); +}; + export const getOptions = (): Promise => { return new Promise((resolve) => { // chrome.storageから取ってきつつ, diff --git a/src/extension-contexts/popup.tsx b/src/extension-contexts/popup.tsx index 360db5a0..4ae7bb87 100644 --- a/src/extension-contexts/popup.tsx +++ b/src/extension-contexts/popup.tsx @@ -25,7 +25,7 @@ import { OptionsBool, } from '../constants'; import { printException } from '../utils'; -import { getOptions } from './options'; +import { getOptions, setOptions } from './options'; /* popup.js */ // ツールバー右に表示される拡張機能のボタンをクリック、または @@ -42,8 +42,8 @@ export const Popup = (props: Props): JSX.Element => { const [enabled, setEnabled] = useState(optionsEnabled); const onSave = useCallback(() => { - chrome.storage.sync.set(enabled); - window.chrome.tabs.query({}, (result) => + setOptions(enabled); + chrome.tabs.query({}, (result) => result.forEach((tab) => { // console.log(tab); if (!(tab.url && tab.id)) { @@ -58,7 +58,7 @@ export const Popup = (props: Props): JSX.Element => { // 送り先タブが拡張機能が動作する対象ではないならメッセージを送らない return; } - window.chrome.tabs.sendMessage(tab.id, { method: OPTION_UPDATED }, (response) => { + chrome.tabs.sendMessage(tab.id, { method: OPTION_UPDATED }, (response) => { // eslint-disable-next-line no-console console.log('res:', response); }); @@ -137,7 +137,7 @@ export const Popup = (props: Props): JSX.Element => { ); }; -getOptions().then(optionsEnabled => { +getOptions().then((optionsEnabled) => { const props = { optionsText: OPTIONS_TEXT, optionKeys: OPTION_KEYS, @@ -156,4 +156,4 @@ getOptions().then(optionsEnabled => { } } ReactDOM.render(, document.getElementById('root')); - }); +}); From 59a8e517ff84ee1f79a44331d44b1679822237f9 Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 01:49:17 +0900 Subject: [PATCH 22/38] use jest-chrome mock --- __tests__/Utils.test.js | 9 ++++++--- __tests__/popup.test.js | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/__tests__/Utils.test.js b/__tests__/Utils.test.js index 787c9211..e9c216a2 100644 --- a/__tests__/Utils.test.js +++ b/__tests__/Utils.test.js @@ -1,4 +1,7 @@ // import * as main from '../src/main'; + +import { chrome } from 'jest-chrome'; + import { OPTION_KEYS, initialOptionsBool } from '../src/constants'; import { printException, @@ -254,7 +257,7 @@ describe('Utils', () => { }); it('初期設定を取得できる', async () => { - window.chrome.runtime.sendMessage = jest.fn((_, callback) => callback({ data: initialOptionsBool })); + chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({ data: initialOptionsBool })); await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool); }); @@ -263,12 +266,12 @@ describe('Utils', () => { OPTION_KEYS.forEach((key, i) => { 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 () => { - window.chrome.runtime.sendMessage = jest.fn((_, callback) => callback({})); + chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({})); await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool); }); }); diff --git a/__tests__/popup.test.js b/__tests__/popup.test.js index 1fa9fa67..85536c3d 100644 --- a/__tests__/popup.test.js +++ b/__tests__/popup.test.js @@ -3,6 +3,7 @@ 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, From 992ebbf1ad02642c92f7d244b453ad198c3ef63d Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 02:32:18 +0900 Subject: [PATCH 23/38] export constant --- src/constants.ts | 3 +++ src/extension-contexts/options.ts | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index a60a1fd4..b090c30a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -103,3 +103,6 @@ export const OPTIONS_TEXT: { [key in keyof Options]: string } = { // 画像ページ STRIP_IMAGE_SUFFIX: '[Ctrl]+[s]で拡張子を校正', }; + +// chrome.storateへの移行が済んだかどうかのキー +export const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; diff --git a/src/extension-contexts/options.ts b/src/extension-contexts/options.ts index 75d670db..6a967228 100644 --- a/src/extension-contexts/options.ts +++ b/src/extension-contexts/options.ts @@ -1,6 +1,11 @@ -import { OptionsBool, initialOptions, initialOptionsBool, OPTION_KEYS, isTrue } from '../constants'; - -const MIGRATED_TO_CHROME_STORAGE = 'MIGRATED_TO_CHROME_STORAGE'; +import { + OptionsBool, + initialOptions, + initialOptionsBool, + OPTION_KEYS, + isTrue, + MIGRATED_TO_CHROME_STORAGE, +} from '../constants'; export const setOptions = (options: OptionsBool): void => { chrome.storage.sync.set(options, () => { From 539b6bd2a6a19fbdc94f463ba3be0614909a59d0 Mon Sep 17 00:00:00 2001 From: hogashi Date: Thu, 5 Jan 2023 02:32:42 +0900 Subject: [PATCH 24/38] WIP adding tests for options --- __tests__/options.test.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 __tests__/options.test.js diff --git a/__tests__/options.test.js b/__tests__/options.test.js new file mode 100644 index 00000000..fefa95bc --- /dev/null +++ b/__tests__/options.test.js @@ -0,0 +1,30 @@ +import { chrome } from 'jest-chrome'; +import { initialOptionsBool, MIGRATED_TO_CHROME_STORAGE } 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(() => chromeStorage); +beforeEach(() => { + chromeStorage = {}; + window.localStorage = {}; +}); + +describe('options', () => { + describe('getOptions', () => { + it('何もない状態で呼んだら, 初期値が返って, 初期値が保存されて, 移行済みになる', () => { + expect(getOptions()).resolves.toMatchObject(initialOptionsBool); + expect(chromeStorage).toMatchObject({ + ...initialOptionsBool, + [MIGRATED_TO_CHROME_STORAGE]: true, + }); + }); + it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => { + }); + it('移行済みなら, 保存された設定が返る', () => { + }); + describe('setOptions', () => {}); +}); From 52f0a47247a1dd7e3cce3090e498eda516c16309 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sat, 7 Jan 2023 01:28:08 +0900 Subject: [PATCH 25/38] jest with colors --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1b94fd42..49661878 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "lint": "rome check --apply-suggested src __tests__", "format": "rome format --write src __tests__", "rome:fix": "run-s lint format", - "jest": "jest --version && jest --verbose --testEnvironment=jsdom --coverage", + "jest": "jest --version && jest --verbose --colors --testEnvironment=jsdom --coverage", "coverage": "make-coverage-badge", "test": "run-s lint test:ts jest coverage", "test:lint-format": "rome check src __tests__ && rome format src __tests__", From 48af02259e19e71434e4fe43ac5f4164a6b3c064 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sat, 7 Jan 2023 01:34:01 +0900 Subject: [PATCH 26/38] jest with colors only in github actions --- .github/workflows/nodejs.yml | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 5ff0e7c3..21883430 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -24,3 +24,5 @@ jobs: run: yarn test env: CI: true + # for jest + FORCE_COLOR: true diff --git a/package.json b/package.json index 49661878..1b94fd42 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "lint": "rome check --apply-suggested src __tests__", "format": "rome format --write src __tests__", "rome:fix": "run-s lint format", - "jest": "jest --version && jest --verbose --colors --testEnvironment=jsdom --coverage", + "jest": "jest --version && jest --verbose --testEnvironment=jsdom --coverage", "coverage": "make-coverage-badge", "test": "run-s lint test:ts jest coverage", "test:lint-format": "rome check src __tests__ && rome format src __tests__", From 01638f617247378f0c163931dbae812b2b503f84 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sat, 7 Jan 2023 02:05:33 +0900 Subject: [PATCH 27/38] remove window prefix --- src/utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 98cf69fb..088df2a8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -193,7 +193,7 @@ export const updateOptions = (): Promise => { // 何かおかしくて設定内容取ってこれなかったらデフォルトということにする resolve(response?.data ? response.data : initialOptionsBool); }; - window.chrome.runtime.sendMessage(request, callback); + chrome.runtime.sendMessage(request, callback); }); }; @@ -245,12 +245,12 @@ export const setOriginalButton = (options: OptionsBool): void => { // これ自体がChrome拡張機能のときだけ設置する // (Chrome拡張機能でないときは設定反映できる機構ないので) if (isNativeChromeExtension()) { - window.chrome.runtime.onMessage.addListener((request, _, sendResponse) => { + chrome.runtime.onMessage.addListener((request, _, sendResponse) => { // Unchecked runtime.lastError みたいなエラーが出ることがあるので, // ひとまず console.log で出すようにしてみている - if (window.chrome.runtime.lastError !== undefined) { + if (chrome.runtime.lastError !== undefined) { // eslint-disable-next-line no-console - console.log(window.chrome.runtime.lastError); + console.log(chrome.runtime.lastError); } if (request.method === OPTION_UPDATED) { updateOptions().then((options) => { From 055021fe9d8126a03682b0e1023104fd6c784428 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sat, 7 Jan 2023 02:08:33 +0900 Subject: [PATCH 28/38] do not change window.chrome when chrome extension test --- __tests__/Utils.test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/__tests__/Utils.test.js b/__tests__/Utils.test.js index e9c216a2..70beee2c 100644 --- a/__tests__/Utils.test.js +++ b/__tests__/Utils.test.js @@ -247,15 +247,6 @@ describe('Utils', () => { describe('updateOptions', () => { describe('Chrome拡張機能のとき', () => { - const originalChrome = window.chrome; - beforeAll(() => { - delete window.chrome; - window.chrome = { runtime: { id: 'id' } }; - }); - afterAll(() => { - window.chrome = originalChrome; - }); - it('初期設定を取得できる', async () => { chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({ data: initialOptionsBool })); await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool); From 30b7e146643908e33299807f239e563658ddedee Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 10:33:47 +0900 Subject: [PATCH 29/38] no window prefix --- src/constants.ts | 2 +- src/extension-contexts/background.ts | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index b090c30a..72b341fc 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -77,7 +77,7 @@ export const isTweetdeck = (): boolean => window.location.hostname === HOST_TWEE export const isImageTab = (): boolean => window.location.hostname === HOST_PBS_TWIMG_COM; /** これ自体がChrome拡張機能かどうか */ -export const isNativeChromeExtension = (): boolean => window.chrome?.runtime?.id !== undefined; +export const isNativeChromeExtension = (): boolean => chrome?.runtime?.id !== undefined; // 設定 diff --git a/src/extension-contexts/background.ts b/src/extension-contexts/background.ts index 4179c681..31456403 100644 --- a/src/extension-contexts/background.ts +++ b/src/extension-contexts/background.ts @@ -4,16 +4,14 @@ import { MessageRequest, MessageResponseBool } from '../utils'; // バックグラウンドで実行される -window.chrome.runtime.onMessage.addListener( - (request: MessageRequest, _, sendResponse: (res: MessageResponseBool) => void) => { - // console.log(chrome.runtime.lastError); - if (request.method === GET_LOCAL_STORAGE) { - getOptions().then((options) => { - sendResponse({ data: options }); - }); - } else { - sendResponse({ data: null }); - } - return true; - }, -); +chrome.runtime.onMessage.addListener((request: MessageRequest, _, sendResponse: (res: MessageResponseBool) => void) => { + // console.log(chrome.runtime.lastError); + if (request.method === GET_LOCAL_STORAGE) { + getOptions().then((options) => { + sendResponse({ data: options }); + }); + } else { + sendResponse({ data: null }); + } + return true; +}); From d3b357737846e2a94d59737dd6979f5f44ce8173 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 10:33:59 +0900 Subject: [PATCH 30/38] mock chrome.runtime.id --- __tests__/Utils.test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__tests__/Utils.test.js b/__tests__/Utils.test.js index 70beee2c..5b957251 100644 --- a/__tests__/Utils.test.js +++ b/__tests__/Utils.test.js @@ -247,6 +247,8 @@ describe('Utils', () => { describe('updateOptions', () => { describe('Chrome拡張機能のとき', () => { + chrome.runtime.id = 'mock'; + it('初期設定を取得できる', async () => { chrome.runtime.sendMessage.mockImplementation((_, callback) => callback({ data: initialOptionsBool })); await expect(updateOptions()).resolves.toStrictEqual(initialOptionsBool); From 09ebaf27ca0a643c4effce911b753baec6156301 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 10:40:13 +0900 Subject: [PATCH 31/38] fix syntax --- __tests__/options.test.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index fefa95bc..ebf89564 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -22,9 +22,8 @@ describe('options', () => { [MIGRATED_TO_CHROME_STORAGE]: true, }); }); - it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => { - }); - it('移行済みなら, 保存された設定が返る', () => { - }); - describe('setOptions', () => {}); + it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => {}); + it('移行済みなら, 保存された設定が返る', () => {}); + describe('setOptions', () => {}); + }); }); From 3965f6ad54079bb9662b9d44420340a369ac229d Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 10:47:32 +0900 Subject: [PATCH 32/38] implement storage get completely --- __tests__/options.test.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index ebf89564..bfd741eb 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -7,7 +7,13 @@ let chromeStorage = {}; chrome.storage.sync.set.mockImplementation((items) => { chromeStorage = { ...chromeStorage, ...items }; }); -chrome.storage.sync.get.mockImplementation(() => chromeStorage); +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(k)))); + } +}); beforeEach(() => { chromeStorage = {}; window.localStorage = {}; From 87a9e5c6a39c62169a0f8755b5c13188b8d46f1d Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 10:57:50 +0900 Subject: [PATCH 33/38] another describe --- __tests__/options.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index bfd741eb..2c303b56 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -30,6 +30,6 @@ describe('options', () => { }); it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => {}); it('移行済みなら, 保存された設定が返る', () => {}); - describe('setOptions', () => {}); }); + describe('setOptions', () => {}); }); From 71f3726b4d1ce1a0bd02f1a674e15fb0a24d49a9 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 11:05:10 +0900 Subject: [PATCH 34/38] add migrate localStorage to chrome.storage test, use localStorage methods --- __tests__/options.test.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index 2c303b56..b20e1a5f 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -1,5 +1,11 @@ import { chrome } from 'jest-chrome'; -import { initialOptionsBool, MIGRATED_TO_CHROME_STORAGE } from '../src/constants'; +import { + initialOptions, + initialOptionsBool, + isFalse, + MIGRATED_TO_CHROME_STORAGE, + SHOW_ON_TWEETDECK_TIMELINE, +} from '../src/constants'; import { getOptions, setOptions } from '../src/extension-contexts/options'; @@ -16,7 +22,7 @@ chrome.storage.sync.get.mockImplementation((keys, callback) => { }); beforeEach(() => { chromeStorage = {}; - window.localStorage = {}; + localStorage.clear(); }); describe('options', () => { @@ -28,7 +34,17 @@ describe('options', () => { [MIGRATED_TO_CHROME_STORAGE]: true, }); }); - it('未移行で, localStorageに設定があったら, localStorageの内容が移行されつつ返って, 移行済みになる', () => {}); + 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('移行済みなら, 保存された設定が返る', () => {}); }); describe('setOptions', () => {}); From c258aafe80e5e3d7e799a0ff7924945133d4658d Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 12:02:59 +0900 Subject: [PATCH 35/38] add migrated test, fix mock implementation --- __tests__/options.test.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index b20e1a5f..934c9e7c 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -5,6 +5,7 @@ import { isFalse, MIGRATED_TO_CHROME_STORAGE, SHOW_ON_TWEETDECK_TIMELINE, + SHOW_ON_TIMELINE, } from '../src/constants'; import { getOptions, setOptions } from '../src/extension-contexts/options'; @@ -17,7 +18,7 @@ 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(k)))); + callback(Object.fromEntries(Object.entries(chromeStorage).filter(([k, _]) => keys.find((key) => k === key)))); } }); beforeEach(() => { @@ -45,7 +46,22 @@ describe('options', () => { [MIGRATED_TO_CHROME_STORAGE]: true, }); }); - it('移行済みなら, 保存された設定が返る', () => {}); + 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', () => {}); }); From 75da80bd279be349ed79540afa556ff8137b0b73 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 12:03:38 +0900 Subject: [PATCH 36/38] update coverage --- coverage/badge.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/badge.svg b/coverage/badge.svg index 05828c95..fdcac574 100644 --- a/coverage/badge.svg +++ b/coverage/badge.svg @@ -1 +1 @@ -Coverage: 87.07%Coverage87.07% \ No newline at end of file +Coverage: 82.58%Coverage82.58% \ No newline at end of file From fa98317cb7793cbd891af7a92b9c1313d85ef322 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 12:04:19 +0900 Subject: [PATCH 37/38] add TODO comment --- __tests__/options.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index 934c9e7c..58880109 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -63,5 +63,7 @@ describe('options', () => { }); }); }); - describe('setOptions', () => {}); + describe('setOptions', () => { + // TODO + }); }); From b627845b2a9149c0ec35cdaa53a8272b9f432a37 Mon Sep 17 00:00:00 2001 From: hogashi Date: Sun, 8 Jan 2023 12:20:07 +0900 Subject: [PATCH 38/38] add setOptions test --- __tests__/options.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/__tests__/options.test.js b/__tests__/options.test.js index 58880109..6f3320fa 100644 --- a/__tests__/options.test.js +++ b/__tests__/options.test.js @@ -6,6 +6,7 @@ import { 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'; @@ -64,6 +65,9 @@ describe('options', () => { }); }); describe('setOptions', () => { - // TODO + 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); }); });