mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-22 01:26:48 +03:00
Upgrade to Manifest V3 (#1714)
* Update Chrome manifest.json * Remove use of window in background * Test devpanel * Inject pageScript using new API * Keep connection from devpanel to background alive * Keep connection from content script to background alive * Replace page action with action * Cleanup syncOptions * Update options to not rely on background page access * Start work on updating popup * Updates * Remove window * Get opening in a separate window working * Remove pageScriptWrap * Add socket to panelStore * Fix tests * Try to use MV3 for Firefox * Fix path * Fix Chrome E2E tests * Revert unintentional change * Skip Electron tests for now Looks like they're still working through stuff in https://github.com/electron/electron/issues/41613 * Better image centering The Firefox popup did not like the old CSS. This is still not perfect, but it's better than it was. * Create shaggy-taxis-cross.md
This commit is contained in:
parent
61ec00f505
commit
83b2c19a11
5
.changeset/shaggy-taxis-cross.md
Normal file
5
.changeset/shaggy-taxis-cross.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'remotedev-redux-devtools-extension': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Upgrade to Manifest V3
|
|
@ -5,7 +5,7 @@ import pug from 'pug';
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
const prod = !args.includes('--dev');
|
const prod = !args.includes('--dev');
|
||||||
|
|
||||||
const commonEsbuildOptions = {
|
await esbuild.build({
|
||||||
bundle: true,
|
bundle: true,
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
outdir: 'dist',
|
outdir: 'dist',
|
||||||
|
@ -15,40 +15,24 @@ const commonEsbuildOptions = {
|
||||||
'process.env.NODE_ENV': prod ? '"production"' : '"development"',
|
'process.env.NODE_ENV': prod ? '"production"' : '"development"',
|
||||||
'process.env.BABEL_ENV': prod ? '"production"' : '"development"',
|
'process.env.BABEL_ENV': prod ? '"production"' : '"development"',
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
await esbuild.build({
|
|
||||||
...commonEsbuildOptions,
|
|
||||||
entryPoints: [
|
entryPoints: [
|
||||||
{ out: 'background.bundle', in: 'src/background/index.ts' },
|
{ out: 'background.bundle', in: 'src/background/index.ts' },
|
||||||
{ out: 'options.bundle', in: 'src/options/index.tsx' },
|
{ out: 'options.bundle', in: 'src/options/index.tsx' },
|
||||||
{ out: 'window.bundle', in: 'src/window/index.tsx' },
|
|
||||||
{ out: 'remote.bundle', in: 'src/remote/index.tsx' },
|
{ out: 'remote.bundle', in: 'src/remote/index.tsx' },
|
||||||
{ out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' },
|
{ out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' },
|
||||||
{ out: 'devtools.bundle', in: 'src/devtools/index.ts' },
|
{ out: 'devtools.bundle', in: 'src/devtools/index.ts' },
|
||||||
{ out: 'content.bundle', in: 'src/contentScript/index.ts' },
|
{ out: 'content.bundle', in: 'src/contentScript/index.ts' },
|
||||||
{ out: 'page.bundle', in: 'src/pageScript/index.ts' },
|
{ out: 'page.bundle', in: 'src/pageScript/index.ts' },
|
||||||
...(prod ? [] : [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }]),
|
|
||||||
],
|
],
|
||||||
loader: {
|
loader: {
|
||||||
'.woff2': 'file',
|
'.woff2': 'file',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (prod) {
|
|
||||||
await esbuild.build({
|
|
||||||
...commonEsbuildOptions,
|
|
||||||
entryPoints: [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }],
|
|
||||||
loader: {
|
|
||||||
'.js': 'text',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
console.log('Creating HTML files...');
|
console.log('Creating HTML files...');
|
||||||
const htmlFiles = ['devpanel', 'devtools', 'options', 'remote', 'window'];
|
const htmlFiles = ['devpanel', 'devtools', 'options', 'remote'];
|
||||||
for (const htmlFile of htmlFiles) {
|
for (const htmlFile of htmlFiles) {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
`dist/${htmlFile}.html`,
|
`dist/${htmlFile}.html`,
|
||||||
|
|
|
@ -3,26 +3,20 @@
|
||||||
"name": "Redux DevTools",
|
"name": "Redux DevTools",
|
||||||
"description": "Redux DevTools for debugging application's state changes.",
|
"description": "Redux DevTools for debugging application's state changes.",
|
||||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||||
"manifest_version": 2,
|
"manifest_version": 3,
|
||||||
"page_action": {
|
"action": {
|
||||||
"default_icon": "img/logo/gray.png",
|
"default_icon": "img/logo/gray.png",
|
||||||
"default_title": "Redux DevTools",
|
"default_title": "Redux DevTools",
|
||||||
"default_popup": "window.html#popup"
|
"default_popup": "devpanel.html#popup"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"devtools-left": {
|
"devtools-window": {
|
||||||
"description": "DevTools window to left"
|
"description": "DevTools window"
|
||||||
},
|
|
||||||
"devtools-right": {
|
|
||||||
"description": "DevTools window to right"
|
|
||||||
},
|
|
||||||
"devtools-bottom": {
|
|
||||||
"description": "DevTools window to bottom"
|
|
||||||
},
|
},
|
||||||
"devtools-remote": {
|
"devtools-remote": {
|
||||||
"description": "Remote DevTools"
|
"description": "Remote DevTools"
|
||||||
},
|
},
|
||||||
"_execute_page_action": {
|
"_execute_action": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+E"
|
"default": "Ctrl+Shift+E"
|
||||||
}
|
}
|
||||||
|
@ -34,36 +28,37 @@
|
||||||
"128": "img/logo/128x128.png"
|
"128": "img/logo/128x128.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options.html",
|
"page": "options.html"
|
||||||
"chrome_style": true
|
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": ["background.bundle.js"],
|
"service_worker": "background.bundle.js"
|
||||||
"persistent": false
|
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"exclude_globs": ["https://www.google*"],
|
"exclude_globs": ["https://www.google*"],
|
||||||
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
"js": ["content.bundle.js"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"all_frames": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"exclude_globs": ["https://www.google*"],
|
||||||
|
"js": ["page.bundle.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true,
|
||||||
|
"world": "MAIN"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"devtools_page": "devtools.html",
|
"devtools_page": "devtools.html",
|
||||||
"web_accessible_resources": ["page.bundle.js"],
|
|
||||||
"externally_connectable": {
|
"externally_connectable": {
|
||||||
"ids": ["*"]
|
"ids": ["*"]
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": ["notifications", "contextMenus", "storage"],
|
||||||
"notifications",
|
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||||
"contextMenus",
|
"content_security_policy": {
|
||||||
"storage",
|
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
||||||
"file:///*",
|
},
|
||||||
"http://*/*",
|
|
||||||
"https://*/*"
|
|
||||||
],
|
|
||||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
|
|
||||||
"update_url": "https://clients2.google.com/service/update2/crx",
|
"update_url": "https://clients2.google.com/service/update2/crx",
|
||||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,22 @@
|
||||||
{
|
{
|
||||||
"version": "3.1.10",
|
"version": "3.1.10",
|
||||||
"name": "Redux DevTools",
|
"name": "Redux DevTools",
|
||||||
"manifest_version": 2,
|
"manifest_version": 3,
|
||||||
"description": "Redux Developer Tools for debugging application state changes.",
|
"description": "Redux Developer Tools for debugging application state changes.",
|
||||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||||
"applications": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "extension@redux.devtools"
|
"id": "extension@redux.devtools"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page_action": {
|
"action": {
|
||||||
"default_icon": "img/logo/38x38.png",
|
"default_icon": "img/logo/38x38.png",
|
||||||
"default_title": "Redux DevTools",
|
"default_title": "Redux DevTools",
|
||||||
"default_popup": "window.html#popup"
|
"default_popup": "devpanel.html#popup"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"devtools-left": {
|
"devtools-window": {
|
||||||
"description": "DevTools window to left"
|
"description": "DevTools window"
|
||||||
},
|
|
||||||
"devtools-right": {
|
|
||||||
"description": "DevTools window to right"
|
|
||||||
},
|
|
||||||
"devtools-bottom": {
|
|
||||||
"description": "DevTools window to bottom"
|
|
||||||
},
|
},
|
||||||
"devtools-remote": {
|
"devtools-remote": {
|
||||||
"description": "Remote DevTools"
|
"description": "Remote DevTools"
|
||||||
|
@ -42,21 +36,22 @@
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
"js": ["content.bundle.js"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"all_frames": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["page.bundle.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true,
|
||||||
|
"world": "MAIN"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"devtools_page": "devtools.html",
|
"devtools_page": "devtools.html",
|
||||||
"web_accessible_resources": ["page.bundle.js"],
|
"permissions": ["notifications", "contextMenus", "tabs", "storage"],
|
||||||
"permissions": [
|
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||||
"notifications",
|
"content_security_policy": {
|
||||||
"contextMenus",
|
"extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
|
||||||
"tabs",
|
}
|
||||||
"storage",
|
|
||||||
"file:///*",
|
|
||||||
"http://*/*",
|
|
||||||
"https://*/*"
|
|
||||||
],
|
|
||||||
"content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
TopButtons,
|
TopButtons,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { GoBroadcast } from 'react-icons/go';
|
import { GoBroadcast } from 'react-icons/go';
|
||||||
import { MdBorderBottom, MdBorderLeft, MdBorderRight } from 'react-icons/md';
|
import { MdOutlineWindow } from 'react-icons/md';
|
||||||
import type { Position } from '../pageScript/api/openWindow';
|
import type { Position } from '../pageScript/api/openWindow';
|
||||||
import type { SingleMessage } from '../background/store/apiMiddleware';
|
import type { SingleMessage } from '../background/store/apiMiddleware';
|
||||||
|
|
||||||
|
@ -98,31 +98,13 @@ class Actions extends Component<Props> {
|
||||||
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
{!window.isElectron && position !== '#left' && (
|
{!window.isElectron && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.openWindow('left');
|
this.openWindow('window');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MdBorderLeft />
|
<MdOutlineWindow />
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!window.isElectron && position !== '#right' && (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
this.openWindow('right');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MdBorderRight />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!window.isElectron && position !== '#bottom' && (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
this.openWindow('bottom');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MdBorderBottom />
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!window.isElectron && (
|
{!window.isElectron && (
|
||||||
|
|
|
@ -2,29 +2,23 @@ import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
|
|
||||||
export function createMenu() {
|
export function createMenu() {
|
||||||
const menus = [
|
const menus = [
|
||||||
{ id: 'devtools-left', title: 'To left' },
|
{ id: 'devtools-window', title: 'Open in a window' },
|
||||||
{ id: 'devtools-right', title: 'To right' },
|
|
||||||
{ id: 'devtools-bottom', title: 'To bottom' },
|
|
||||||
{
|
|
||||||
id: 'devtools-panel',
|
|
||||||
title: 'Open in a panel (enable in browser settings)',
|
|
||||||
},
|
|
||||||
{ id: 'devtools-remote', title: 'Open Remote DevTools' },
|
{ id: 'devtools-remote', title: 'Open Remote DevTools' },
|
||||||
];
|
];
|
||||||
|
|
||||||
let shortcuts: { [commandName: string]: string | undefined } = {};
|
let shortcuts: { [commandName: string]: string | undefined } = {};
|
||||||
chrome.commands.getAll((commands) => {
|
chrome.commands.getAll((commands) => {
|
||||||
commands.forEach(({ name, shortcut }) => {
|
for (const { name, shortcut } of commands) {
|
||||||
shortcuts[name!] = shortcut;
|
shortcuts[name!] = shortcut;
|
||||||
});
|
}
|
||||||
|
|
||||||
menus.forEach(({ id, title }) => {
|
for (const { id, title } of menus) {
|
||||||
chrome.contextMenus.create({
|
chrome.contextMenus.create({
|
||||||
id: id,
|
id: id,
|
||||||
title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),
|
title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),
|
||||||
contexts: ['all'],
|
contexts: ['all'],
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,22 @@
|
||||||
import '../chromeApiMock';
|
import configureStore from './store/backgroundStore';
|
||||||
import { Store } from 'redux';
|
|
||||||
import configureStore, { BackgroundAction } from './store/backgroundStore';
|
|
||||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
import { createMenu, removeMenu } from './contextMenus';
|
import { createMenu, removeMenu } from './contextMenus';
|
||||||
import syncOptions from '../options/syncOptions';
|
import { getOptions } from '../options/syncOptions';
|
||||||
import { BackgroundState } from './store/backgroundReducer';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
store: Store<BackgroundState, BackgroundAction>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expose the extension's store globally to access it from the windows
|
// Expose the extension's store globally to access it from the windows
|
||||||
// via chrome.runtime.getBackgroundPage
|
// via chrome.runtime.getBackgroundPage
|
||||||
window.store = configureStore();
|
export const store = configureStore();
|
||||||
|
|
||||||
// Listen for keyboard shortcuts
|
// Listen for keyboard shortcuts
|
||||||
chrome.commands.onCommand.addListener((shortcut) => {
|
chrome.commands.onCommand.addListener((shortcut) => {
|
||||||
openDevToolsWindow(shortcut as DevToolsPosition);
|
openDevToolsWindow(shortcut as DevToolsPosition);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the context menu when installed
|
// Disable the action by default and create the context menu when installed
|
||||||
chrome.runtime.onInstalled.addListener(() => {
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
syncOptions().get((option) => {
|
chrome.action.disable();
|
||||||
|
|
||||||
|
getOptions((option) => {
|
||||||
if (option.showContextMenus) createMenu();
|
if (option.showContextMenus) createMenu();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { LIFTED_ACTION } from '@redux-devtools/app';
|
import { LIFTED_ACTION } from '@redux-devtools/app';
|
||||||
|
import { store } from './index';
|
||||||
|
|
||||||
export function getReport(
|
export function getReport(
|
||||||
reportId: string,
|
reportId: string,
|
||||||
|
@ -24,7 +25,7 @@ export function getReport(
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
const { payload, preloadedState } = json;
|
const { payload, preloadedState } = json;
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
window.store.dispatch({
|
store.dispatch({
|
||||||
type: LIFTED_ACTION,
|
type: LIFTED_ACTION,
|
||||||
message: 'IMPORT',
|
message: 'IMPORT',
|
||||||
state: JSON.stringify({ payload, preloadedState }),
|
state: JSON.stringify({ payload, preloadedState }),
|
||||||
|
|
|
@ -1,83 +1,34 @@
|
||||||
export type DevToolsPosition =
|
export type DevToolsPosition = 'devtools-window' | 'devtools-remote';
|
||||||
| 'devtools-left'
|
|
||||||
| 'devtools-right'
|
|
||||||
| 'devtools-bottom'
|
|
||||||
| 'devtools-panel'
|
|
||||||
| 'devtools-remote';
|
|
||||||
|
|
||||||
let windows: { [K in DevToolsPosition]?: number } = {};
|
let windows: { [K in DevToolsPosition]?: number } = {};
|
||||||
let lastPosition: DevToolsPosition | null = null;
|
|
||||||
|
|
||||||
export default function openDevToolsWindow(position: DevToolsPosition) {
|
export default function openDevToolsWindow(position: DevToolsPosition) {
|
||||||
function popWindow(
|
|
||||||
action: string,
|
|
||||||
url: string,
|
|
||||||
customOptions: chrome.windows.CreateData & chrome.windows.UpdateInfo,
|
|
||||||
) {
|
|
||||||
function focusIfExist(callback: () => void) {
|
|
||||||
if (!windows[position]) {
|
if (!windows[position]) {
|
||||||
callback();
|
createWindow(position);
|
||||||
lastPosition = position;
|
|
||||||
} else {
|
} else {
|
||||||
let params = { focused: true };
|
chrome.windows.update(windows[position]!, { focused: true }, () => {
|
||||||
if (lastPosition !== position && position !== 'devtools-panel') {
|
if (chrome.runtime.lastError) createWindow(position);
|
||||||
params = { ...params, ...customOptions };
|
|
||||||
}
|
|
||||||
chrome.windows.update(windows[position]!, params, () => {
|
|
||||||
lastPosition = null;
|
|
||||||
if (chrome.runtime.lastError) callback();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
focusIfExist(() => {
|
function createWindow(position: DevToolsPosition) {
|
||||||
let options: chrome.windows.CreateData = {
|
const url = chrome.runtime.getURL(getPath(position));
|
||||||
type: 'popup',
|
chrome.windows.create({ type: 'popup', url }, (win) => {
|
||||||
...customOptions,
|
|
||||||
};
|
|
||||||
if (action === 'open') {
|
|
||||||
options.url = chrome.extension.getURL(
|
|
||||||
url + '#' + position.substr(position.indexOf('-') + 1),
|
|
||||||
);
|
|
||||||
chrome.windows.create(options, (win) => {
|
|
||||||
windows[position] = win!.id;
|
windows[position] = win!.id;
|
||||||
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||||
chrome.windows.update(win!.id!, {
|
chrome.windows.update(win!.id!, { focused: true });
|
||||||
focused: true,
|
|
||||||
...customOptions,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let params: chrome.windows.CreateData & chrome.windows.UpdateInfo = {
|
function getPath(position: DevToolsPosition) {
|
||||||
left: 0,
|
|
||||||
top: 0,
|
|
||||||
width: 380,
|
|
||||||
height: window.screen.availHeight,
|
|
||||||
};
|
|
||||||
let url = 'window.html';
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 'devtools-right':
|
case 'devtools-window':
|
||||||
params.left =
|
return 'devpanel.html';
|
||||||
(window.screen as unknown as { availLeft: number }).availLeft +
|
|
||||||
window.screen.availWidth -
|
|
||||||
params.width!;
|
|
||||||
break;
|
|
||||||
case 'devtools-bottom':
|
|
||||||
params.height = 420;
|
|
||||||
params.top = window.screen.height - params.height;
|
|
||||||
params.width = window.screen.availWidth;
|
|
||||||
break;
|
|
||||||
case 'devtools-panel':
|
|
||||||
params.type = 'panel';
|
|
||||||
break;
|
|
||||||
case 'devtools-remote':
|
case 'devtools-remote':
|
||||||
params = { width: 850, height: 600 };
|
return 'remote.html';
|
||||||
url = 'remote.html';
|
default:
|
||||||
break;
|
throw new Error(`Unrecognized position: ${position}`);
|
||||||
}
|
}
|
||||||
popWindow('open', url, params);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,7 @@ import {
|
||||||
TOGGLE_PERSIST,
|
TOGGLE_PERSIST,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import syncOptions, {
|
import type { Options, OptionsMessage } from '../../options/syncOptions';
|
||||||
Options,
|
|
||||||
OptionsMessage,
|
|
||||||
SyncOptions,
|
|
||||||
} from '../../options/syncOptions';
|
|
||||||
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
||||||
import { getReport } from '../logging';
|
import { getReport } from '../logging';
|
||||||
import { Action, Dispatch, Middleware } from 'redux';
|
import { Action, Dispatch, Middleware } from 'redux';
|
||||||
|
@ -32,6 +28,7 @@ import { LiftedState } from '@redux-devtools/instrument';
|
||||||
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
||||||
import type { Position } from '../../pageScript/api/openWindow';
|
import type { Position } from '../../pageScript/api/openWindow';
|
||||||
import type { BackgroundState } from './backgroundReducer';
|
import type { BackgroundState } from './backgroundReducer';
|
||||||
|
import { store } from '../index';
|
||||||
|
|
||||||
interface TabMessageBase {
|
interface TabMessageBase {
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
|
@ -51,6 +48,11 @@ interface StopAction extends TabMessageBase {
|
||||||
readonly id?: never;
|
readonly id?: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OptionsAction {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
|
readonly options: Options;
|
||||||
|
}
|
||||||
|
|
||||||
interface DispatchAction extends TabMessageBase {
|
interface DispatchAction extends TabMessageBase {
|
||||||
readonly type: 'DISPATCH';
|
readonly type: 'DISPATCH';
|
||||||
readonly action: AppDispatchAction;
|
readonly action: AppDispatchAction;
|
||||||
|
@ -196,7 +198,7 @@ interface SplitUpdateStateAction<S, A extends Action<string>> {
|
||||||
export type TabMessage =
|
export type TabMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
| OptionsMessage
|
| OptionsAction
|
||||||
| DispatchAction
|
| DispatchAction
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
|
@ -247,7 +249,6 @@ const chunks: {
|
||||||
>;
|
>;
|
||||||
} = {};
|
} = {};
|
||||||
let monitors = 0;
|
let monitors = 0;
|
||||||
let isMonitored = false;
|
|
||||||
|
|
||||||
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
||||||
sender.tab ? sender.tab.id! : name || sender.id!;
|
sender.tab ? sender.tab.id! : name || sender.id!;
|
||||||
|
@ -261,22 +262,18 @@ type MonitorAction<S, A extends Action<string>> =
|
||||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||||
const maxChromeMsgSize = 32 * 1024 * 1024;
|
const maxChromeMsgSize = 32 * 1024 * 1024;
|
||||||
|
|
||||||
|
// TODO Clean up args
|
||||||
function toMonitors<S, A extends Action<string>>(
|
function toMonitors<S, A extends Action<string>>(
|
||||||
action: MonitorAction<S, A>,
|
action: MonitorAction<S, A>,
|
||||||
tabId?: string | number,
|
tabId?: string | number,
|
||||||
verbose?: boolean,
|
verbose?: boolean,
|
||||||
) {
|
) {
|
||||||
for (const monitorPort of Object.values(connections.monitor)) {
|
for (const port of [
|
||||||
monitorPort.postMessage(
|
...Object.values(connections.monitor),
|
||||||
verbose || action.type === 'ERROR' || action.type === SET_PERSIST
|
...Object.values(connections.panel),
|
||||||
? action
|
]) {
|
||||||
: { type: UPDATE_STATE },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const panelPort of Object.values(connections.panel)) {
|
|
||||||
try {
|
try {
|
||||||
panelPort.postMessage(action);
|
port.postMessage(action);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (
|
if (
|
||||||
action.type !== UPDATE_STATE ||
|
action.type !== UPDATE_STATE ||
|
||||||
|
@ -307,11 +304,11 @@ function toMonitors<S, A extends Action<string>>(
|
||||||
value;
|
value;
|
||||||
}
|
}
|
||||||
|
|
||||||
panelPort.postMessage({ ...action, request: splitMessageStart });
|
port.postMessage({ ...action, request: splitMessageStart });
|
||||||
|
|
||||||
for (let i = 0; i < toSplit.length; i++) {
|
for (let i = 0; i < toSplit.length; i++) {
|
||||||
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
||||||
panelPort.postMessage({
|
port.postMessage({
|
||||||
...action,
|
...action,
|
||||||
request: {
|
request: {
|
||||||
split: 'chunk',
|
split: 'chunk',
|
||||||
|
@ -324,7 +321,7 @@ function toMonitors<S, A extends Action<string>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
panelPort.postMessage({ ...action, request: { split: 'end' } });
|
port.postMessage({ ...action, request: { split: 'end' } });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,7 +343,7 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
window.store,
|
store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as AppDispatchAction,
|
action as AppDispatchAction,
|
||||||
|
@ -360,7 +357,7 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
window.store,
|
store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
|
@ -374,7 +371,7 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
window.store,
|
store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
|
@ -388,7 +385,7 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
window.store,
|
store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
|
@ -402,7 +399,7 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
window.store,
|
store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as AppDispatchAction,
|
action as AppDispatchAction,
|
||||||
|
@ -414,14 +411,12 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAllTabs(msg: TabMessage) {
|
function toAllTabs(msg: TabMessage) {
|
||||||
const tabs = connections.tab;
|
for (const tabPort of Object.values(connections.tab)) {
|
||||||
Object.keys(tabs).forEach((id) => {
|
tabPort.postMessage(msg);
|
||||||
tabs[id].postMessage(msg);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||||
if (!id && isMonitored === shouldMonitor) return;
|
|
||||||
const action = {
|
const action = {
|
||||||
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
||||||
};
|
};
|
||||||
|
@ -430,11 +425,10 @@ function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||||
} else {
|
} else {
|
||||||
toAllTabs(action);
|
toAllTabs(action);
|
||||||
}
|
}
|
||||||
isMonitored = shouldMonitor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReducerError() {
|
function getReducerError() {
|
||||||
const instancesState = window.store.getState().instances;
|
const instancesState = store.getState().instances;
|
||||||
const payload = instancesState.states[instancesState.current];
|
const payload = instancesState.states[instancesState.current];
|
||||||
const computedState = payload.computedStates[payload.currentStateIndex];
|
const computedState = payload.computedStates[payload.currentStateIndex];
|
||||||
if (!computedState) return false;
|
if (!computedState) return false;
|
||||||
|
@ -442,11 +436,11 @@ function getReducerError() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePersist() {
|
function togglePersist() {
|
||||||
const state = window.store.getState();
|
const state = store.getState();
|
||||||
if (state.instances.persisted) {
|
if (state.instances.persisted) {
|
||||||
Object.keys(state.instances.connections).forEach((id) => {
|
Object.keys(state.instances.connections).forEach((id) => {
|
||||||
if (connections.tab[id]) return;
|
if (connections.tab[id]) return;
|
||||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||||
toMonitors({ type: 'NA', id });
|
toMonitors({ type: 'NA', id });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -461,34 +455,25 @@ interface OpenOptionsMessage {
|
||||||
readonly type: 'OPEN_OPTIONS';
|
readonly type: 'OPEN_OPTIONS';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetOptionsMessage {
|
export type SingleMessage = OpenMessage | OpenOptionsMessage | OptionsMessage;
|
||||||
readonly type: 'GET_OPTIONS';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SingleMessage =
|
|
||||||
| OpenMessage
|
|
||||||
| OpenOptionsMessage
|
|
||||||
| GetOptionsMessage;
|
|
||||||
|
|
||||||
type BackgroundStoreMessage<S, A extends Action<string>> =
|
type BackgroundStoreMessage<S, A extends Action<string>> =
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||||
| SplitMessage
|
| SplitMessage
|
||||||
| SingleMessage;
|
| SingleMessage;
|
||||||
type BackgroundStoreResponse = { readonly options: Options };
|
|
||||||
|
|
||||||
// Receive messages from content scripts
|
// Receive messages from content scripts
|
||||||
function messaging<S, A extends Action<string>>(
|
function messaging<S, A extends Action<string>>(
|
||||||
request: BackgroundStoreMessage<S, A>,
|
request: BackgroundStoreMessage<S, A>,
|
||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
sendResponse?: (response?: BackgroundStoreResponse) => void,
|
|
||||||
) {
|
) {
|
||||||
let tabId = getId(sender);
|
let tabId = getId(sender);
|
||||||
if (!tabId) return;
|
if (!tabId) return;
|
||||||
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
||||||
|
|
||||||
if (request.type === 'STOP') {
|
if (request.type === 'STOP') {
|
||||||
if (!Object.keys(window.store.getState().instances.connections).length) {
|
if (!Object.keys(store.getState().instances.connections).length) {
|
||||||
window.store.dispatch({ type: DISCONNECTED });
|
store.dispatch({ type: DISCONNECTED });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -496,10 +481,8 @@ function messaging<S, A extends Action<string>>(
|
||||||
chrome.runtime.openOptionsPage();
|
chrome.runtime.openOptionsPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'GET_OPTIONS') {
|
if (request.type === 'OPTIONS') {
|
||||||
window.syncOptions.get((options) => {
|
toAllTabs({ type: 'OPTIONS', options: request.options });
|
||||||
sendResponse!({ options });
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'GET_REPORT') {
|
if (request.type === 'GET_REPORT') {
|
||||||
|
@ -507,12 +490,8 @@ function messaging<S, A extends Action<string>>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'OPEN') {
|
if (request.type === 'OPEN') {
|
||||||
let position: DevToolsPosition = 'devtools-left';
|
let position: DevToolsPosition = 'devtools-window';
|
||||||
if (
|
if (['remote', 'window'].includes(request.position)) {
|
||||||
['remote', 'panel', 'left', 'right', 'bottom'].indexOf(
|
|
||||||
request.position,
|
|
||||||
) !== -1
|
|
||||||
) {
|
|
||||||
position = ('devtools-' + request.position) as DevToolsPosition;
|
position = ('devtools-' + request.position) as DevToolsPosition;
|
||||||
}
|
}
|
||||||
openDevToolsWindow(position);
|
openDevToolsWindow(position);
|
||||||
|
@ -560,7 +539,7 @@ function messaging<S, A extends Action<string>>(
|
||||||
if (request.instanceId) {
|
if (request.instanceId) {
|
||||||
action.request.instanceId = instanceId;
|
action.request.instanceId = instanceId;
|
||||||
}
|
}
|
||||||
window.store.dispatch(action);
|
store.dispatch(action);
|
||||||
|
|
||||||
if (request.type === 'EXPORT') {
|
if (request.type === 'EXPORT') {
|
||||||
toMonitors(action, tabId, true);
|
toMonitors(action, tabId, true);
|
||||||
|
@ -580,8 +559,8 @@ function disconnect(
|
||||||
if (p) p.onDisconnect.removeListener(disconnectListener);
|
if (p) p.onDisconnect.removeListener(disconnectListener);
|
||||||
delete connections[type][id];
|
delete connections[type][id];
|
||||||
if (type === 'tab') {
|
if (type === 'tab') {
|
||||||
if (!window.store.getState().instances.persisted) {
|
if (!store.getState().instances.persisted) {
|
||||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||||
toMonitors({ type: 'NA', id });
|
toMonitors({ type: 'NA', id });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -595,21 +574,22 @@ function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||||
let id: number | string;
|
let id: number | string;
|
||||||
let listener;
|
let listener;
|
||||||
|
|
||||||
window.store.dispatch({ type: CONNECTED, port });
|
store.dispatch({ type: CONNECTED, port });
|
||||||
|
|
||||||
if (port.name === 'tab') {
|
if (port.name === 'tab') {
|
||||||
id = getId(port.sender!);
|
id = getId(port.sender!);
|
||||||
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
||||||
connections.tab[id] = port;
|
connections.tab[id] = port;
|
||||||
listener = (msg: ContentScriptToBackgroundMessage<S, A>) => {
|
listener = (msg: ContentScriptToBackgroundMessage<S, A> | 'heartbeat') => {
|
||||||
|
if (msg === 'heartbeat') return;
|
||||||
if (msg.name === 'INIT_INSTANCE') {
|
if (msg.name === 'INIT_INSTANCE') {
|
||||||
if (typeof id === 'number') {
|
if (typeof id === 'number') {
|
||||||
chrome.pageAction.show(id);
|
chrome.action.enable(id);
|
||||||
chrome.pageAction.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
chrome.action.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
||||||
}
|
}
|
||||||
if (isMonitored) port.postMessage({ type: 'START' });
|
port.postMessage({ type: 'START' });
|
||||||
|
|
||||||
const state = window.store.getState();
|
const state = store.getState();
|
||||||
if (state.instances.persisted) {
|
if (state.instances.persisted) {
|
||||||
const instanceId = `${id}/${msg.instanceId}`;
|
const instanceId = `${id}/${msg.instanceId}`;
|
||||||
const persistedState = state.instances.states[instanceId];
|
const persistedState = state.instances.states[instanceId];
|
||||||
|
@ -636,6 +616,11 @@ function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||||
id = getId(port.sender!, port.name);
|
id = getId(port.sender!, port.name);
|
||||||
connections.monitor[id] = port;
|
connections.monitor[id] = port;
|
||||||
monitorInstances(true);
|
monitorInstances(true);
|
||||||
|
listener = (msg: BackgroundAction | 'heartbeat') => {
|
||||||
|
if (msg === 'heartbeat') return;
|
||||||
|
store.dispatch(msg);
|
||||||
|
};
|
||||||
|
port.onMessage.addListener(listener);
|
||||||
monitors++;
|
monitors++;
|
||||||
port.onDisconnect.addListener(disconnect('monitor', id));
|
port.onDisconnect.addListener(disconnect('monitor', id));
|
||||||
} else {
|
} else {
|
||||||
|
@ -644,8 +629,9 @@ function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||||
connections.panel[id] = port;
|
connections.panel[id] = port;
|
||||||
monitorInstances(true, port.name);
|
monitorInstances(true, port.name);
|
||||||
monitors++;
|
monitors++;
|
||||||
listener = (msg: BackgroundAction) => {
|
listener = (msg: BackgroundAction | 'heartbeat') => {
|
||||||
window.store.dispatch(msg);
|
if (msg === 'heartbeat') return;
|
||||||
|
store.dispatch(msg);
|
||||||
};
|
};
|
||||||
port.onMessage.addListener(listener);
|
port.onMessage.addListener(listener);
|
||||||
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
||||||
|
@ -659,17 +645,9 @@ chrome.runtime.onMessageExternal.addListener(messaging);
|
||||||
|
|
||||||
chrome.notifications.onClicked.addListener((id) => {
|
chrome.notifications.onClicked.addListener((id) => {
|
||||||
chrome.notifications.clear(id);
|
chrome.notifications.clear(id);
|
||||||
openDevToolsWindow('devtools-right');
|
openDevToolsWindow('devtools-window');
|
||||||
});
|
});
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
syncOptions: SyncOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
|
||||||
|
|
||||||
const api: Middleware<{}, BackgroundState, Dispatch<BackgroundAction>> =
|
const api: Middleware<{}, BackgroundState, Dispatch<BackgroundAction>> =
|
||||||
(store) => (next) => (untypedAction) => {
|
(store) => (next) => (untypedAction) => {
|
||||||
const action = untypedAction as BackgroundAction;
|
const action = untypedAction as BackgroundAction;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import '../chromeApiMock';
|
import '../chromeApiMock';
|
||||||
import {
|
import {
|
||||||
injectOptions,
|
getOptions,
|
||||||
getOptionsFromBg,
|
|
||||||
isAllowed,
|
isAllowed,
|
||||||
|
Options,
|
||||||
|
prefetchOptions,
|
||||||
|
prepareOptionsForPage,
|
||||||
} from '../options/syncOptions';
|
} from '../options/syncOptions';
|
||||||
import type { TabMessage } from '../background/store/apiMiddleware';
|
import type { TabMessage } from '../background/store/apiMiddleware';
|
||||||
import type {
|
import type {
|
||||||
|
@ -84,6 +86,13 @@ interface UpdateAction {
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OptionsAction {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
|
readonly options: Options;
|
||||||
|
readonly id: undefined;
|
||||||
|
readonly source: typeof source;
|
||||||
|
}
|
||||||
|
|
||||||
export type ContentScriptToPageScriptMessage =
|
export type ContentScriptToPageScriptMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
|
@ -91,7 +100,8 @@ export type ContentScriptToPageScriptMessage =
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction;
|
| UpdateAction
|
||||||
|
| OptionsAction;
|
||||||
|
|
||||||
interface ImportStatePayload<S, A extends Action<string>> {
|
interface ImportStatePayload<S, A extends Action<string>> {
|
||||||
readonly type: 'IMPORT_STATE';
|
readonly type: 'IMPORT_STATE';
|
||||||
|
@ -112,6 +122,7 @@ export type ListenerMessage<S, A extends Action<string>> =
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction
|
| UpdateAction
|
||||||
|
| OptionsAction
|
||||||
| ImportStateDispatchAction<S, A>;
|
| ImportStateDispatchAction<S, A>;
|
||||||
|
|
||||||
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
||||||
|
@ -156,8 +167,13 @@ function connect() {
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if ('options' in message) {
|
} else if (message.type === 'OPTIONS') {
|
||||||
injectOptions(message.options);
|
postToPageScript({
|
||||||
|
type: message.type,
|
||||||
|
options: prepareOptionsForPage(message.options),
|
||||||
|
id: undefined,
|
||||||
|
source,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
postToPageScript({
|
postToPageScript({
|
||||||
type: message.type,
|
type: message.type,
|
||||||
|
@ -289,7 +305,14 @@ function send<S, A extends Action<string>>(
|
||||||
) {
|
) {
|
||||||
if (!connected) connect();
|
if (!connected) connect();
|
||||||
if (message.type === 'INIT_INSTANCE') {
|
if (message.type === 'INIT_INSTANCE') {
|
||||||
getOptionsFromBg();
|
getOptions((options) => {
|
||||||
|
postToPageScript({
|
||||||
|
type: 'OPTIONS',
|
||||||
|
options: prepareOptionsForPage(options),
|
||||||
|
id: undefined,
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
});
|
||||||
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||||
} else {
|
} else {
|
||||||
postToBackground({ name: 'RELAY', message });
|
postToBackground({ name: 'RELAY', message });
|
||||||
|
@ -317,4 +340,10 @@ function handleMessages<S, A extends Action<string>>(
|
||||||
tryCatch(send, message);
|
tryCatch(send, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefetchOptions();
|
||||||
|
|
||||||
window.addEventListener('message', handleMessages, false);
|
window.addEventListener('message', handleMessages, false);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
bg?.postMessage('heartbeat');
|
||||||
|
}, 15000);
|
||||||
|
|
|
@ -5,12 +5,13 @@ html
|
||||||
meta(charset='UTF-8')
|
meta(charset='UTF-8')
|
||||||
title Redux DevTools
|
title Redux DevTools
|
||||||
include ../style.pug
|
include ../style.pug
|
||||||
style.
|
|
||||||
body {
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body
|
body
|
||||||
#root
|
#root
|
||||||
|
div(style='display: flex; justify-content: center; align-items: center')
|
||||||
|
img(
|
||||||
|
src='/img/loading.svg',
|
||||||
|
height=300, width=350,
|
||||||
|
)
|
||||||
link(href='/devpanel.bundle.css', rel='stylesheet')
|
link(href='/devpanel.bundle.css', rel='stylesheet')
|
||||||
script(src='/devpanel.bundle.js')
|
script(src='/devpanel.bundle.js')
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Persistor } from 'redux-persist';
|
||||||
import {
|
import {
|
||||||
REMOVE_INSTANCE,
|
REMOVE_INSTANCE,
|
||||||
StoreAction,
|
StoreAction,
|
||||||
|
StoreState,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import App from '../app/App';
|
import App from '../app/App';
|
||||||
|
@ -18,19 +19,19 @@ import {
|
||||||
SplitUpdateStateRequest,
|
SplitUpdateStateRequest,
|
||||||
UpdateStateRequest,
|
UpdateStateRequest,
|
||||||
} from '../background/store/apiMiddleware';
|
} from '../background/store/apiMiddleware';
|
||||||
import type { StoreStateWithoutSocket } from './store/panelReducer';
|
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
|
|
||||||
const position = location.hash;
|
const position = location.hash;
|
||||||
const messageStyle: CSSProperties = {
|
const messageStyle: CSSProperties = {
|
||||||
padding: '20px',
|
paddingTop: '20px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
|
boxSizing: 'border-box',
|
||||||
};
|
};
|
||||||
|
|
||||||
let rendered: boolean | undefined;
|
let rendered: boolean | undefined;
|
||||||
let currentRoot: Root | undefined;
|
let currentRoot: Root | undefined;
|
||||||
let store: Store<StoreStateWithoutSocket, StoreAction> | undefined;
|
let store: Store<StoreState, StoreAction> | undefined;
|
||||||
let persistor: Persistor | undefined;
|
let persistor: Persistor | undefined;
|
||||||
let bgConnection: chrome.runtime.Port;
|
let bgConnection: chrome.runtime.Port;
|
||||||
let naTimeout: NodeJS.Timeout;
|
let naTimeout: NodeJS.Timeout;
|
||||||
|
@ -72,7 +73,12 @@ function renderNA() {
|
||||||
.
|
.
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
if (isChrome) {
|
if (
|
||||||
|
isChrome &&
|
||||||
|
chrome &&
|
||||||
|
chrome.devtools &&
|
||||||
|
chrome.devtools.inspectedWindow
|
||||||
|
) {
|
||||||
chrome.devtools.inspectedWindow.getResources((resources) => {
|
chrome.devtools.inspectedWindow.getResources((resources) => {
|
||||||
if (resources[0].url.substr(0, 4) === 'file') {
|
if (resources[0].url.substr(0, 4) === 'file') {
|
||||||
message = (
|
message = (
|
||||||
|
@ -101,17 +107,26 @@ function renderNA() {
|
||||||
|
|
||||||
let splitMessage: SplitUpdateStateRequest<unknown, Action<string>>;
|
let splitMessage: SplitUpdateStateRequest<unknown, Action<string>>;
|
||||||
|
|
||||||
function init(id: number) {
|
function init() {
|
||||||
renderNA();
|
renderNA();
|
||||||
bgConnection = chrome.runtime.connect({
|
|
||||||
name: id ? id.toString() : undefined,
|
let name = 'monitor';
|
||||||
});
|
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||||
|
name += chrome.devtools.inspectedWindow.tabId;
|
||||||
|
}
|
||||||
|
bgConnection = chrome.runtime.connect({ name });
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
bgConnection.postMessage('heartbeat');
|
||||||
|
}, 15000);
|
||||||
|
|
||||||
bgConnection.onMessage.addListener(
|
bgConnection.onMessage.addListener(
|
||||||
<S, A extends Action<string>>(
|
<S, A extends Action<string>>(
|
||||||
message: PanelMessageWithSplitAction<S, A>,
|
message: PanelMessageWithSplitAction<S, A>,
|
||||||
) => {
|
) => {
|
||||||
if (message.type === 'NA') {
|
if (message.type === 'NA') {
|
||||||
if (message.id === id) renderNA();
|
// TODO Double-check this now that the name is different
|
||||||
|
if (message.id === name) renderNA();
|
||||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||||
} else {
|
} else {
|
||||||
if (!rendered) renderDevTools();
|
if (!rendered) renderDevTools();
|
||||||
|
@ -157,4 +172,7 @@ function init(id: number) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
init(chrome.devtools.inspectedWindow.tabId);
|
if (position === '#popup') document.body.style.minWidth = '760px';
|
||||||
|
if (position !== '#popup') document.body.style.minHeight = '100%';
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
|
@ -1,45 +1,29 @@
|
||||||
import { combineReducers, Reducer } from 'redux';
|
import { combineReducers, Reducer } from 'redux';
|
||||||
import {
|
import {
|
||||||
connection,
|
connection,
|
||||||
ConnectionState,
|
|
||||||
instances,
|
instances,
|
||||||
InstancesState,
|
|
||||||
monitor,
|
monitor,
|
||||||
MonitorState,
|
|
||||||
notification,
|
notification,
|
||||||
NotificationState,
|
|
||||||
reports,
|
reports,
|
||||||
ReportsState,
|
|
||||||
section,
|
section,
|
||||||
SectionState,
|
socket,
|
||||||
StateTreeSettings,
|
|
||||||
stateTreeSettings,
|
stateTreeSettings,
|
||||||
StoreAction,
|
StoreAction,
|
||||||
|
StoreState,
|
||||||
theme,
|
theme,
|
||||||
ThemeState,
|
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
|
|
||||||
export interface StoreStateWithoutSocket {
|
|
||||||
readonly section: SectionState;
|
|
||||||
readonly theme: ThemeState;
|
|
||||||
readonly connection: ConnectionState;
|
|
||||||
readonly monitor: MonitorState;
|
|
||||||
readonly instances: InstancesState;
|
|
||||||
readonly reports: ReportsState;
|
|
||||||
readonly notification: NotificationState;
|
|
||||||
readonly stateTreeSettings: StateTreeSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rootReducer: Reducer<
|
const rootReducer: Reducer<
|
||||||
StoreStateWithoutSocket,
|
StoreState,
|
||||||
StoreAction,
|
StoreAction,
|
||||||
Partial<StoreStateWithoutSocket>
|
Partial<StoreState>
|
||||||
> = combineReducers({
|
> = combineReducers({
|
||||||
instances,
|
instances,
|
||||||
monitor,
|
monitor,
|
||||||
reports,
|
reports,
|
||||||
notification,
|
notification,
|
||||||
section,
|
section,
|
||||||
|
socket,
|
||||||
theme,
|
theme,
|
||||||
connection,
|
connection,
|
||||||
stateTreeSettings,
|
stateTreeSettings,
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
import { createStore, applyMiddleware, Reducer, Store } from 'redux';
|
import { createStore, applyMiddleware, Reducer, Store } from 'redux';
|
||||||
import localForage from 'localforage';
|
import localForage from 'localforage';
|
||||||
import { persistReducer, persistStore } from 'redux-persist';
|
import { persistReducer, persistStore } from 'redux-persist';
|
||||||
import { exportStateMiddleware, StoreAction } from '@redux-devtools/app';
|
import {
|
||||||
|
exportStateMiddleware,
|
||||||
|
StoreAction,
|
||||||
|
StoreState,
|
||||||
|
} from '@redux-devtools/app';
|
||||||
import panelDispatcher from './panelSyncMiddleware';
|
import panelDispatcher from './panelSyncMiddleware';
|
||||||
import rootReducer, { StoreStateWithoutSocket } from './panelReducer';
|
import rootReducer from './panelReducer';
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
key: 'redux-devtools',
|
key: 'redux-devtools',
|
||||||
|
@ -11,8 +15,10 @@ const persistConfig = {
|
||||||
storage: localForage,
|
storage: localForage,
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistedReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
|
const persistedReducer: Reducer<StoreState, StoreAction> = persistReducer(
|
||||||
persistReducer(persistConfig, rootReducer) as any;
|
persistConfig,
|
||||||
|
rootReducer,
|
||||||
|
) as any;
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
position: string,
|
position: string,
|
||||||
|
|
|
@ -7,23 +7,51 @@ import {
|
||||||
TOGGLE_PERSIST,
|
TOGGLE_PERSIST,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { Dispatch, Middleware } from 'redux';
|
import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
|
||||||
|
|
||||||
|
function selectInstance(
|
||||||
|
tabId: number,
|
||||||
|
store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>,
|
||||||
|
next: (action: unknown) => unknown,
|
||||||
|
) {
|
||||||
|
const instances = store.getState().instances;
|
||||||
|
if (instances.current === 'default') return;
|
||||||
|
const connections = instances.connections[tabId];
|
||||||
|
if (connections && connections.length === 1) {
|
||||||
|
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentTabId(next: (tabId: number) => void) {
|
||||||
|
chrome.tabs.query(
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
lastFocusedWindow: true,
|
||||||
|
},
|
||||||
|
(tabs) => {
|
||||||
|
const tab = tabs[0];
|
||||||
|
if (!tab) return;
|
||||||
|
next(tab.id!);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function panelDispatcher(
|
function panelDispatcher(
|
||||||
bgConnection: chrome.runtime.Port,
|
bgConnection: chrome.runtime.Port,
|
||||||
): Middleware<{}, StoreState, Dispatch<StoreAction>> {
|
): Middleware<{}, StoreState, Dispatch<StoreAction>> {
|
||||||
let autoselected = false;
|
let autoselected = false;
|
||||||
const tabId = chrome.devtools.inspectedWindow.tabId;
|
|
||||||
|
|
||||||
return (store) => (next) => (untypedAction) => {
|
return (store) => (next) => (untypedAction) => {
|
||||||
const action = untypedAction as StoreAction;
|
const action = untypedAction as StoreAction;
|
||||||
|
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
if (!autoselected && action.type === UPDATE_STATE && tabId) {
|
if (!autoselected && action.type === UPDATE_STATE) {
|
||||||
autoselected = true;
|
autoselected = true;
|
||||||
const connections = store.getState().instances.connections[tabId];
|
|
||||||
if (connections && connections.length === 1) {
|
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
selectInstance(chrome.devtools.inspectedWindow.tabId, store, next);
|
||||||
|
} else {
|
||||||
|
getCurrentTabId((tabId) => selectInstance(tabId, store, next));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
||||||
|
|
|
@ -1,17 +1,6 @@
|
||||||
function createPanel(url: string) {
|
|
||||||
chrome.devtools.panels.create(
|
chrome.devtools.panels.create(
|
||||||
'Redux',
|
'Redux',
|
||||||
'img/logo/scalable.png',
|
'img/logo/scalable.png',
|
||||||
url,
|
'devpanel.html',
|
||||||
function () {},
|
() => {},
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (chrome.runtime.getBackgroundPage) {
|
|
||||||
// Check if the background page's object is accessible (not in incognito)
|
|
||||||
chrome.runtime.getBackgroundPage((background) => {
|
|
||||||
createPanel(background ? 'window.html' : 'devpanel.html');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
createPanel('devpanel.html');
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,22 +2,25 @@ import '../chromeApiMock';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import OptionsComponent from './Options';
|
import OptionsComponent from './Options';
|
||||||
import { Options } from './syncOptions';
|
import {
|
||||||
|
getOptions,
|
||||||
|
Options,
|
||||||
|
OptionsMessage,
|
||||||
|
saveOption,
|
||||||
|
subscribeToOptions,
|
||||||
|
} from './syncOptions';
|
||||||
|
|
||||||
chrome.runtime.getBackgroundPage((background) => {
|
subscribeToOptions((options) => {
|
||||||
const syncOptions = background!.syncOptions;
|
const message: OptionsMessage = { type: 'OPTIONS', options };
|
||||||
|
chrome.runtime.sendMessage(message);
|
||||||
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
});
|
||||||
syncOptions.save(name, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderOptions = (options: Options) => {
|
const renderOptions = (options: Options) => {
|
||||||
const root = createRoot(document.getElementById('root')!);
|
const root = createRoot(document.getElementById('root')!);
|
||||||
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
syncOptions.subscribe(renderOptions);
|
subscribeToOptions(renderOptions);
|
||||||
syncOptions.get((options) => {
|
getOptions((options) => {
|
||||||
renderOptions(options);
|
renderOptions(options);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
|
@ -38,20 +38,21 @@ let options: Options | undefined;
|
||||||
let subscribers: ((options: Options) => void)[] = [];
|
let subscribers: ((options: Options) => void)[] = [];
|
||||||
|
|
||||||
export interface OptionsMessage {
|
export interface OptionsMessage {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
readonly options: Options;
|
readonly options: Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToAllTabs = (msg: OptionsMessage) => void;
|
export const saveOption = <K extends keyof Options>(
|
||||||
|
key: K,
|
||||||
const save =
|
value: Options[K],
|
||||||
(toAllTabs: ToAllTabs | undefined) =>
|
) => {
|
||||||
<K extends keyof Options>(key: K, value: Options[K]) => {
|
|
||||||
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
||||||
obj[key] = value;
|
obj[key] = value;
|
||||||
chrome.storage.sync.set(obj);
|
chrome.storage.sync.set(obj);
|
||||||
options![key] = value;
|
options![key] = value;
|
||||||
toAllTabs!({ options: options! });
|
for (const subscriber of subscribers) {
|
||||||
subscribers.forEach((s) => s(options!));
|
subscriber(options!);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
|
@ -71,7 +72,7 @@ const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
: oldOptions.filter,
|
: oldOptions.filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const get = (callback: (options: Options) => void) => {
|
export const getOptions = (callback: (options: Options) => void) => {
|
||||||
if (options) callback(options);
|
if (options) callback(options);
|
||||||
else {
|
else {
|
||||||
chrome.storage.sync.get(
|
chrome.storage.sync.get(
|
||||||
|
@ -98,67 +99,29 @@ const get = (callback: (options: Options) => void) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscribe = (callback: (options: Options) => void) => {
|
export const prefetchOptions = () => getOptions(() => {});
|
||||||
|
|
||||||
|
export const subscribeToOptions = (callback: (options: Options) => void) => {
|
||||||
subscribers = subscribers.concat(callback);
|
subscribers = subscribers.concat(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toReg = (str: string) =>
|
const toReg = (str: string) =>
|
||||||
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
||||||
|
|
||||||
export const injectOptions = (newOptions: Options) => {
|
export const prepareOptionsForPage = (options: Options): Options => ({
|
||||||
if (!newOptions) return;
|
...options,
|
||||||
|
|
||||||
options = {
|
|
||||||
...newOptions,
|
|
||||||
allowlist:
|
allowlist:
|
||||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
options.filter !== FilterState.DO_NOT_FILTER
|
||||||
? toReg(newOptions.allowlist)!
|
? toReg(options.allowlist)!
|
||||||
: newOptions.allowlist,
|
: options.allowlist,
|
||||||
denylist:
|
denylist:
|
||||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
options.filter !== FilterState.DO_NOT_FILTER
|
||||||
? toReg(newOptions.denylist)!
|
? toReg(options.denylist)!
|
||||||
: newOptions.denylist,
|
: options.denylist,
|
||||||
};
|
|
||||||
let s = document.createElement('script');
|
|
||||||
s.type = 'text/javascript';
|
|
||||||
s.appendChild(
|
|
||||||
document.createTextNode(
|
|
||||||
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
|
|
||||||
JSON.stringify(options) +
|
|
||||||
');',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
(document.head || document.documentElement).appendChild(s);
|
|
||||||
s.parentNode!.removeChild(s);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getOptionsFromBg = () => {
|
|
||||||
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
|
|
||||||
if (response && response.options) injectOptions(response.options);
|
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
get((newOptions) => {
|
|
||||||
injectOptions(newOptions);
|
|
||||||
}); // Legacy
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isAllowed = (localOptions = options) =>
|
export const isAllowed = (localOptions = options) =>
|
||||||
!localOptions ||
|
!localOptions ||
|
||||||
localOptions.inject ||
|
localOptions.inject ||
|
||||||
!localOptions.urls ||
|
!localOptions.urls ||
|
||||||
location.href.match(toReg(localOptions.urls)!);
|
location.href.match(toReg(localOptions.urls)!);
|
||||||
|
|
||||||
export interface SyncOptions {
|
|
||||||
readonly save: <K extends keyof Options>(key: K, value: Options[K]) => void;
|
|
||||||
readonly get: (callback: (options: Options) => void) => void;
|
|
||||||
readonly subscribe: (callback: (options: Options) => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function syncOptions(toAllTabs?: ToAllTabs): SyncOptions {
|
|
||||||
if (toAllTabs && !options) get(() => {}); // Initialize
|
|
||||||
return {
|
|
||||||
save: save(toAllTabs),
|
|
||||||
get: get,
|
|
||||||
subscribe: subscribe,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import type { PageScriptToContentScriptMessage } from './index';
|
import type { PageScriptToContentScriptMessage } from './index';
|
||||||
|
|
||||||
export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote';
|
export type Position = 'window' | 'remote';
|
||||||
|
|
||||||
function post<S, A extends Action<string>>(
|
function post<S, A extends Action<string>>(
|
||||||
message: PageScriptToContentScriptMessage<S, A>,
|
message: PageScriptToContentScriptMessage<S, A>,
|
||||||
|
@ -13,6 +13,6 @@ export default function openWindow(position?: Position) {
|
||||||
post({
|
post({
|
||||||
source: '@devtools-page',
|
source: '@devtools-page',
|
||||||
type: 'OPEN',
|
type: 'OPEN',
|
||||||
position: position || 'right',
|
position: position ?? 'window',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -432,6 +432,13 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
serializeAction,
|
serializeAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
case 'OPTIONS':
|
||||||
|
window.devToolsOptions = Object.assign(
|
||||||
|
window.devToolsOptions || {},
|
||||||
|
message.options,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
// @ts-ignore
|
|
||||||
import script from '../dist/page.bundle.js';
|
|
||||||
|
|
||||||
let s = document.createElement('script');
|
|
||||||
s.type = 'text/javascript';
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
s.appendChild(document.createTextNode(script));
|
|
||||||
(document.head || document.documentElement).appendChild(s);
|
|
||||||
s.parentNode!.removeChild(s);
|
|
||||||
} else {
|
|
||||||
s.src = chrome.extension.getURL('page.bundle.js');
|
|
||||||
s.onload = function () {
|
|
||||||
(this as HTMLScriptElement).parentNode!.removeChild(
|
|
||||||
this as HTMLScriptElement,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(document.head || document.documentElement).appendChild(s);
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { createRoot } from 'react-dom/client';
|
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
|
||||||
import { UPDATE_STATE } from '@redux-devtools/app';
|
|
||||||
import App from '../app/App';
|
|
||||||
import configureStore from './store/windowStore';
|
|
||||||
import type { MonitorMessage } from '../background/store/apiMiddleware';
|
|
||||||
|
|
||||||
const position = location.hash;
|
|
||||||
|
|
||||||
chrome.runtime.getBackgroundPage((window) => {
|
|
||||||
const { store } = window!;
|
|
||||||
const { store: localStore, persistor } = configureStore(store, position);
|
|
||||||
let name = 'monitor';
|
|
||||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
|
||||||
name += chrome.devtools.inspectedWindow.tabId;
|
|
||||||
}
|
|
||||||
const bg = chrome.runtime.connect({ name });
|
|
||||||
const update = (action?: MonitorMessage) => {
|
|
||||||
localStore.dispatch(action || { type: UPDATE_STATE });
|
|
||||||
};
|
|
||||||
bg.onMessage.addListener(update);
|
|
||||||
update();
|
|
||||||
|
|
||||||
const root = createRoot(document.getElementById('root')!);
|
|
||||||
root.render(
|
|
||||||
<Provider store={localStore}>
|
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
|
||||||
<App position={position} />
|
|
||||||
</PersistGate>
|
|
||||||
</Provider>,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (position === '#popup') document.body.style.minWidth = '760px';
|
|
||||||
if (position !== '#popup') document.body.style.minHeight = '100%';
|
|
|
@ -1,51 +0,0 @@
|
||||||
import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
|
|
||||||
import {
|
|
||||||
SELECT_INSTANCE,
|
|
||||||
StoreAction,
|
|
||||||
StoreState,
|
|
||||||
UPDATE_STATE,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
|
|
||||||
function selectInstance(
|
|
||||||
tabId: number,
|
|
||||||
store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>,
|
|
||||||
next: (action: unknown) => unknown,
|
|
||||||
) {
|
|
||||||
const instances = store.getState().instances;
|
|
||||||
if (instances.current === 'default') return;
|
|
||||||
const connections = instances.connections[tabId];
|
|
||||||
if (connections && connections.length === 1) {
|
|
||||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentTabId(next: (tabId: number) => void) {
|
|
||||||
chrome.tabs.query(
|
|
||||||
{
|
|
||||||
active: true,
|
|
||||||
lastFocusedWindow: true,
|
|
||||||
},
|
|
||||||
(tabs) => {
|
|
||||||
const tab = tabs[0];
|
|
||||||
if (!tab) return;
|
|
||||||
next(tab.id!);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const popupSelector: Middleware<{}, StoreState, Dispatch<StoreAction>> =
|
|
||||||
(store) => (next) => (untypedAction) => {
|
|
||||||
const action = untypedAction as StoreAction;
|
|
||||||
|
|
||||||
const result = next(action);
|
|
||||||
if (action.type === UPDATE_STATE) {
|
|
||||||
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
|
||||||
selectInstance(chrome.devtools.inspectedWindow.tabId, store, next);
|
|
||||||
} else {
|
|
||||||
getCurrentTabId((tabId) => selectInstance(tabId, store, next));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default popupSelector;
|
|
|
@ -1,34 +0,0 @@
|
||||||
import {
|
|
||||||
instancesInitialState,
|
|
||||||
dispatchAction,
|
|
||||||
UPDATE_STATE,
|
|
||||||
SELECT_INSTANCE,
|
|
||||||
LIFTED_ACTION,
|
|
||||||
SET_PERSIST,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import type {
|
|
||||||
ExpandedUpdateStateAction,
|
|
||||||
WindowStoreAction,
|
|
||||||
} from './windowStore';
|
|
||||||
|
|
||||||
export default function instances(
|
|
||||||
state = instancesInitialState,
|
|
||||||
action: WindowStoreAction,
|
|
||||||
) {
|
|
||||||
switch (action.type) {
|
|
||||||
case UPDATE_STATE:
|
|
||||||
return {
|
|
||||||
...(action as ExpandedUpdateStateAction).instances,
|
|
||||||
selected: state.selected,
|
|
||||||
};
|
|
||||||
case LIFTED_ACTION:
|
|
||||||
if (action.message === 'DISPATCH') return dispatchAction(state, action);
|
|
||||||
return state;
|
|
||||||
case SELECT_INSTANCE:
|
|
||||||
return { ...state, selected: action.selected };
|
|
||||||
case SET_PERSIST:
|
|
||||||
return { ...state, persisted: action.payload };
|
|
||||||
default:
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { combineReducers, Reducer } from 'redux';
|
|
||||||
import {
|
|
||||||
connection,
|
|
||||||
monitor,
|
|
||||||
notification,
|
|
||||||
reports,
|
|
||||||
section,
|
|
||||||
socket,
|
|
||||||
theme,
|
|
||||||
stateTreeSettings,
|
|
||||||
StoreState,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import instances from './instancesReducer';
|
|
||||||
import type { WindowStoreAction } from './windowStore';
|
|
||||||
|
|
||||||
const rootReducer: Reducer<
|
|
||||||
StoreState,
|
|
||||||
WindowStoreAction,
|
|
||||||
Partial<StoreState>
|
|
||||||
> = combineReducers({
|
|
||||||
instances,
|
|
||||||
monitor,
|
|
||||||
socket,
|
|
||||||
reports,
|
|
||||||
notification,
|
|
||||||
section,
|
|
||||||
theme,
|
|
||||||
connection,
|
|
||||||
stateTreeSettings,
|
|
||||||
}) as any;
|
|
||||||
|
|
||||||
export default rootReducer;
|
|
|
@ -1,81 +0,0 @@
|
||||||
import {
|
|
||||||
createStore,
|
|
||||||
compose,
|
|
||||||
applyMiddleware,
|
|
||||||
Store,
|
|
||||||
StoreEnhancer,
|
|
||||||
Reducer,
|
|
||||||
} from 'redux';
|
|
||||||
import localForage from 'localforage';
|
|
||||||
import { persistReducer, persistStore } from 'redux-persist';
|
|
||||||
import {
|
|
||||||
api,
|
|
||||||
CONNECT_REQUEST,
|
|
||||||
exportStateMiddleware,
|
|
||||||
InstancesState,
|
|
||||||
StoreActionWithoutUpdateState,
|
|
||||||
StoreState,
|
|
||||||
UpdateStateAction,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import syncStores from './windowSyncMiddleware';
|
|
||||||
import instanceSelector from './instanceSelectorMiddleware';
|
|
||||||
import rootReducer from './windowReducer';
|
|
||||||
import type { BackgroundState } from '../../background/store/backgroundReducer';
|
|
||||||
import type { BackgroundAction } from '../../background/store/backgroundStore';
|
|
||||||
import type {
|
|
||||||
EmptyUpdateStateAction,
|
|
||||||
NAAction,
|
|
||||||
} from '../../background/store/apiMiddleware';
|
|
||||||
|
|
||||||
export interface ExpandedUpdateStateAction extends UpdateStateAction {
|
|
||||||
readonly instances: InstancesState;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type WindowStoreAction =
|
|
||||||
| StoreActionWithoutUpdateState
|
|
||||||
| ExpandedUpdateStateAction
|
|
||||||
| NAAction
|
|
||||||
| EmptyUpdateStateAction;
|
|
||||||
|
|
||||||
const persistConfig = {
|
|
||||||
key: 'redux-devtools',
|
|
||||||
blacklist: ['instances', 'socket'],
|
|
||||||
storage: localForage,
|
|
||||||
};
|
|
||||||
|
|
||||||
const persistedReducer: Reducer<StoreState, WindowStoreAction> = persistReducer(
|
|
||||||
persistConfig,
|
|
||||||
rootReducer,
|
|
||||||
) as any;
|
|
||||||
|
|
||||||
export default function configureStore(
|
|
||||||
baseStore: Store<BackgroundState, BackgroundAction>,
|
|
||||||
position: string,
|
|
||||||
) {
|
|
||||||
let enhancer: StoreEnhancer;
|
|
||||||
const middlewares = [exportStateMiddleware, api, syncStores(baseStore)];
|
|
||||||
if (!position || position === '#popup') {
|
|
||||||
// select current tab instance for devPanel and pageAction
|
|
||||||
middlewares.push(instanceSelector);
|
|
||||||
}
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
enhancer = applyMiddleware(...middlewares);
|
|
||||||
} else {
|
|
||||||
enhancer = compose(
|
|
||||||
applyMiddleware(...middlewares),
|
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__
|
|
||||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
|
||||||
: (noop: unknown) => noop,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const store = createStore(persistedReducer, enhancer);
|
|
||||||
const persistor = persistStore(store as Store, null, () => {
|
|
||||||
if (store.getState().connection.type !== 'disabled') {
|
|
||||||
store.dispatch({
|
|
||||||
type: CONNECT_REQUEST,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return { store, persistor };
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
import {
|
|
||||||
getActiveInstance,
|
|
||||||
LIFTED_ACTION,
|
|
||||||
StoreAction,
|
|
||||||
StoreState,
|
|
||||||
TOGGLE_PERSIST,
|
|
||||||
UPDATE_STATE,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import { Dispatch, Middleware, Store } from 'redux';
|
|
||||||
import type { BackgroundState } from '../../background/store/backgroundReducer';
|
|
||||||
import type { BackgroundAction } from '../../background/store/backgroundStore';
|
|
||||||
|
|
||||||
const syncStores =
|
|
||||||
(
|
|
||||||
baseStore: Store<BackgroundState, BackgroundAction>,
|
|
||||||
): Middleware<{}, StoreState, Dispatch<StoreAction>> =>
|
|
||||||
(store) =>
|
|
||||||
(next) =>
|
|
||||||
(untypedAction) => {
|
|
||||||
const action = untypedAction as StoreAction;
|
|
||||||
|
|
||||||
if (action.type === UPDATE_STATE) {
|
|
||||||
return next({
|
|
||||||
...action,
|
|
||||||
instances: baseStore.getState().instances,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
|
||||||
const instances = store.getState().instances;
|
|
||||||
const instanceId = getActiveInstance(instances);
|
|
||||||
const id = instances.options[instanceId].connectionId;
|
|
||||||
baseStore.dispatch({ ...action, instanceId, id } as any);
|
|
||||||
}
|
|
||||||
return next(action);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default syncStores;
|
|
|
@ -1,18 +0,0 @@
|
||||||
doctype html
|
|
||||||
|
|
||||||
html
|
|
||||||
head
|
|
||||||
meta(charset='UTF-8')
|
|
||||||
title Redux DevTools
|
|
||||||
include ../style.pug
|
|
||||||
|
|
||||||
body
|
|
||||||
#root
|
|
||||||
div(style='position: relative')
|
|
||||||
img(
|
|
||||||
src='/img/loading.svg',
|
|
||||||
height=300, width=350,
|
|
||||||
style='position: absolute; top: 50%; left: 50%; margin-top: -175px; margin-left: -175px;'
|
|
||||||
)
|
|
||||||
link(href='/window.bundle.css', rel='stylesheet')
|
|
||||||
script(src='/window.bundle.js')
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, within } from '@testing-library/react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import configureStore from '../../../src/window/store/windowStore';
|
import configureStore from '../../../src/devpanel/store/panelStore';
|
||||||
import App from '../../../src/app/App';
|
import App from '../../../src/app/App';
|
||||||
|
|
||||||
Object.defineProperty(window, 'matchMedia', {
|
Object.defineProperty(window, 'matchMedia', {
|
||||||
|
|
|
@ -20,16 +20,7 @@ describe('API', () => {
|
||||||
expect(message).toEqual({
|
expect(message).toEqual({
|
||||||
source: '@devtools-page',
|
source: '@devtools-page',
|
||||||
type: 'OPEN',
|
type: 'OPEN',
|
||||||
position: 'right',
|
position: 'window',
|
||||||
});
|
|
||||||
|
|
||||||
message = await listenMessage(() => {
|
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.open('left');
|
|
||||||
});
|
|
||||||
expect(message).toEqual({
|
|
||||||
source: '@devtools-page',
|
|
||||||
type: 'OPEN',
|
|
||||||
position: 'left',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,9 @@ describe('Chrome extension', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should open extension's window", async () => {
|
it("should open extension's window", async () => {
|
||||||
await driver.get(`chrome-extension://${extensionId}/window.html#left`);
|
await driver.get(`chrome-extension://${extensionId}/devpanel.html`);
|
||||||
const url = await driver.getCurrentUrl();
|
const url = await driver.getCurrentUrl();
|
||||||
expect(url).toBe(`chrome-extension://${extensionId}/window.html#left`);
|
expect(url).toBe(`chrome-extension://${extensionId}/devpanel.html`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match document title', async () => {
|
it('should match document title', async () => {
|
||||||
|
@ -37,25 +37,6 @@ describe('Chrome extension', function () {
|
||||||
expect(title).toBe('Redux DevTools');
|
expect(title).toBe('Redux DevTools');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should contain inspector monitor's component", async () => {
|
|
||||||
await delay(1000);
|
|
||||||
const val = await driver
|
|
||||||
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
|
|
||||||
.getText();
|
|
||||||
expect(val).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should contain an empty actions list', async () => {
|
|
||||||
const val = await driver
|
|
||||||
.findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]'))
|
|
||||||
.getText();
|
|
||||||
expect(val).toBe('');
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(switchMonitorTests).forEach((description) =>
|
|
||||||
it(description, () => switchMonitorTests[description](driver)),
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should get actions list', async () => {
|
it('should get actions list', async () => {
|
||||||
const url = 'https://zalmoxisus.github.io/examples/router/';
|
const url = 'https://zalmoxisus.github.io/examples/router/';
|
||||||
await driver.executeScript(`window.open('${url}')`);
|
await driver.executeScript(`window.open('${url}')`);
|
||||||
|
@ -68,6 +49,7 @@ describe('Chrome extension', function () {
|
||||||
|
|
||||||
await driver.switchTo().window(tabs[0]);
|
await driver.switchTo().window(tabs[0]);
|
||||||
|
|
||||||
|
await delay(1000);
|
||||||
const result = await driver.wait(
|
const result = await driver.wait(
|
||||||
driver
|
driver
|
||||||
.findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]'))
|
.findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]'))
|
||||||
|
@ -80,4 +62,15 @@ describe('Chrome extension', function () {
|
||||||
);
|
);
|
||||||
expect(result).toBeTruthy();
|
expect(result).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should contain inspector monitor's component", async () => {
|
||||||
|
const val = await driver
|
||||||
|
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
|
||||||
|
.getText();
|
||||||
|
expect(val).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(switchMonitorTests).forEach((description) =>
|
||||||
|
it(description, () => switchMonitorTests[description](driver)),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -76,7 +76,7 @@ describe('DevTools panel for Electron', function () {
|
||||||
expect(className).not.toMatch(/hidden/); // not hidden
|
expect(className).not.toMatch(/hidden/); // not hidden
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have Redux DevTools UI on current tab', async () => {
|
it.skip('should have Redux DevTools UI on current tab', async () => {
|
||||||
await driver
|
await driver
|
||||||
.switchTo()
|
.switchTo()
|
||||||
.frame(
|
.frame(
|
||||||
|
@ -87,7 +87,7 @@ describe('DevTools panel for Electron', function () {
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain INIT action', async () => {
|
it.skip('should contain INIT action', async () => {
|
||||||
const element = await driver.wait(
|
const element = await driver.wait(
|
||||||
webdriver.until.elementLocated(
|
webdriver.until.elementLocated(
|
||||||
webdriver.By.xpath('//div[@data-testid="actionListRows"]'),
|
webdriver.By.xpath('//div[@data-testid="actionListRows"]'),
|
||||||
|
@ -99,7 +99,7 @@ describe('DevTools panel for Electron', function () {
|
||||||
expect(val).toMatch(/@@INIT/);
|
expect(val).toMatch(/@@INIT/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should contain Inspector monitor's component", async () => {
|
it.skip("should contain Inspector monitor's component", async () => {
|
||||||
const val = await driver
|
const val = await driver
|
||||||
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
|
.findElement(webdriver.By.xpath('//div[@data-testid="inspector"]'))
|
||||||
.getText();
|
.getText();
|
||||||
|
@ -107,7 +107,7 @@ describe('DevTools panel for Electron', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.keys(switchMonitorTests).forEach((description) =>
|
Object.keys(switchMonitorTests).forEach((description) =>
|
||||||
it(description, () => switchMonitorTests[description](driver)),
|
it.skip(description, () => switchMonitorTests[description](driver)),
|
||||||
);
|
);
|
||||||
|
|
||||||
/* it('should be no logs in console of main window', async () => {
|
/* it('should be no logs in console of main window', async () => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user