test
Some checks failed
test / Run tests (push) Failing after 6s
build / Build (push) Failing after 9s
Some checks failed
test / Run tests (push) Failing after 6s
build / Build (push) Failing after 9s
This commit is contained in:
parent
c2219db73e
commit
fd2aba4286
@ -56,7 +56,6 @@
|
||||
"@types/serviceworker": "^0.0.120",
|
||||
"@unocss/postcss": "0.65.0-beta.2",
|
||||
"@vitest/coverage-v8": "2.1.4",
|
||||
"@webext-core/fake-browser": "^1.3.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"compression-webpack-plugin": "^11.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -66,6 +65,7 @@
|
||||
"fake-indexeddb": "^6.0.0",
|
||||
"globals": "^15.14.0",
|
||||
"jsdom": "^25.0.1",
|
||||
"mock-xmlhttprequest": "^8.4.1",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"prettier": "^3.4.2",
|
||||
|
3
packages/chrome-extension-mock/README.md
Normal file
3
packages/chrome-extension-mock/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# mock一个chrome扩展环境
|
||||
> 只针对自己的项目做了一些简单的封装,如果有需要可以自己修改
|
||||
|
32
packages/chrome-extension-mock/cookies.ts
Normal file
32
packages/chrome-extension-mock/cookies.ts
Normal file
@ -0,0 +1,32 @@
|
||||
export default class Cookies {
|
||||
getAllCookieStores(
|
||||
callback: (cookieStores: chrome.cookies.CookieStore[]) => void
|
||||
) {
|
||||
callback([
|
||||
{
|
||||
id: "0",
|
||||
tabIds: [1],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
mockGetAll?: (
|
||||
details: chrome.cookies.GetAllDetails,
|
||||
callback: (cookies: chrome.cookies.Cookie[]) => void
|
||||
) => void | undefined;
|
||||
|
||||
getAll(
|
||||
details: chrome.cookies.GetAllDetails,
|
||||
callback: (cookies: chrome.cookies.Cookie[]) => void
|
||||
): void {
|
||||
this.mockGetAll?.(details, callback);
|
||||
}
|
||||
|
||||
set(details: chrome.cookies.SetDetails, callback?: () => void): void {
|
||||
callback?.();
|
||||
}
|
||||
|
||||
remove(details: chrome.cookies.Details, callback?: () => void): void {
|
||||
callback?.();
|
||||
}
|
||||
}
|
38
packages/chrome-extension-mock/declarativ_net_request.ts
Normal file
38
packages/chrome-extension-mock/declarativ_net_request.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export default class DeclarativeNetRequest {
|
||||
HeaderOperation = {
|
||||
APPEND: "append",
|
||||
SET: "set",
|
||||
REMOVE: "remove",
|
||||
};
|
||||
|
||||
RuleActionType = {
|
||||
BLOCK: "block",
|
||||
REDIRECT: "redirect",
|
||||
ALLOW: "allow",
|
||||
UPGRADE_SCHEME: "upgradeScheme",
|
||||
MODIFY_HEADERS: "modifyHeaders",
|
||||
ALLOW_ALL_REQUESTS: "allowAllRequests",
|
||||
};
|
||||
|
||||
ResourceType = {
|
||||
MAIN_FRAME: "main_frame",
|
||||
SUB_FRAME: "sub_frame",
|
||||
STYLESHEET: "stylesheet",
|
||||
SCRIPT: "script",
|
||||
IMAGE: "image",
|
||||
FONT: "font",
|
||||
OBJECT: "object",
|
||||
XMLHTTPREQUEST: "xmlhttprequest",
|
||||
PING: "ping",
|
||||
CSP_REPORT: "csp_report",
|
||||
MEDIA: "media",
|
||||
WEBSOCKET: "websocket",
|
||||
OTHER: "other",
|
||||
};
|
||||
|
||||
updateSessionRules() {
|
||||
return new Promise<void>((resolve) => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
}
|
5
packages/chrome-extension-mock/downloads.ts
Normal file
5
packages/chrome-extension-mock/downloads.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default class Downloads {
|
||||
download(_: any, callback: Function) {
|
||||
callback && callback();
|
||||
}
|
||||
}
|
9
packages/chrome-extension-mock/i18n.ts
Normal file
9
packages/chrome-extension-mock/i18n.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export default class I18n {
|
||||
getUILanguage() {
|
||||
return "zh-CN";
|
||||
}
|
||||
|
||||
getAcceptLanguages(callback: (lngs: string[]) => void) {
|
||||
callback(["zh-CN"]);
|
||||
}
|
||||
}
|
26
packages/chrome-extension-mock/index.ts
Normal file
26
packages/chrome-extension-mock/index.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import Cookies from "./cookies";
|
||||
import Downloads from "./downloads";
|
||||
import Notifications from "./notifications";
|
||||
import Runtime from "./runtime";
|
||||
import MockTab from "./tab";
|
||||
import WebRequest from "./web_reqeuest";
|
||||
import Storage from "./storage";
|
||||
import I18n from "./i18n";
|
||||
import DeclarativeNetRequest from "./declarativ_net_request";
|
||||
|
||||
const chromeMock = {
|
||||
tabs: new MockTab(),
|
||||
runtime: new Runtime(),
|
||||
webRequest: new WebRequest(),
|
||||
notifications: new Notifications(),
|
||||
downloads: new Downloads(),
|
||||
cookies: new Cookies(),
|
||||
storage: new Storage(),
|
||||
i18n: new I18n(),
|
||||
declarativeNetRequest: new DeclarativeNetRequest(),
|
||||
init() {},
|
||||
};
|
||||
// @ts-ignore
|
||||
global.chrome = chromeMock;
|
||||
|
||||
export default chromeMock;
|
64
packages/chrome-extension-mock/notifications.ts
Normal file
64
packages/chrome-extension-mock/notifications.ts
Normal file
@ -0,0 +1,64 @@
|
||||
export default class Notifications {
|
||||
notification: Map<string, boolean> = new Map();
|
||||
|
||||
onClosedHandler?: (id: string, byUser: boolean) => void;
|
||||
|
||||
onClosed = {
|
||||
addListener: (
|
||||
callback: (notificationId: string, byUser: boolean) => void
|
||||
) => {
|
||||
this.onClosedHandler = callback;
|
||||
},
|
||||
};
|
||||
|
||||
onButtonClickedHandler?: (id: string, index: number) => void;
|
||||
|
||||
onButtonClicked = {
|
||||
addListener: (
|
||||
callback: (notificationId: string, buttonIndex: number) => void
|
||||
) => {
|
||||
this.onButtonClickedHandler = callback;
|
||||
},
|
||||
};
|
||||
|
||||
mockClickButton(id: string, index: number) {
|
||||
this.onButtonClickedHandler?.(id, index);
|
||||
}
|
||||
|
||||
onClickedHandler?: (id: string) => void;
|
||||
|
||||
onClicked = {
|
||||
addListener: (callback: (notificationId: string) => void) => {
|
||||
this.onClickedHandler = callback;
|
||||
},
|
||||
};
|
||||
|
||||
create(
|
||||
options: chrome.notifications.NotificationOptions,
|
||||
callback?: (id: string) => void
|
||||
) {
|
||||
const id = Math.random().toString();
|
||||
this.notification.set(id, true);
|
||||
if (callback) {
|
||||
callback(id);
|
||||
}
|
||||
}
|
||||
|
||||
clear(id: string) {
|
||||
if (!this.notification.has(id)) {
|
||||
throw new Error("notification not found");
|
||||
}
|
||||
this.notification.delete(id);
|
||||
}
|
||||
|
||||
update(id: string) {
|
||||
if (!this.notification.has(id)) {
|
||||
throw new Error("notification not found");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
mockClick(id: string) {
|
||||
this.onClickedHandler?.(id);
|
||||
}
|
||||
}
|
62
packages/chrome-extension-mock/runtime.ts
Normal file
62
packages/chrome-extension-mock/runtime.ts
Normal file
@ -0,0 +1,62 @@
|
||||
type Port = chrome.runtime.Port & {
|
||||
setTargetPort: (port: chrome.runtime.Port) => void;
|
||||
messageListener: Array<(message: any) => void>;
|
||||
};
|
||||
|
||||
export default class Runtime {
|
||||
connectListener: Array<(port: chrome.runtime.Port) => void> = [];
|
||||
|
||||
onConnect = {
|
||||
addListener: (callback: (port: chrome.runtime.Port) => void) => {
|
||||
this.connectListener.push(callback);
|
||||
},
|
||||
};
|
||||
|
||||
Port(connectInfo?: chrome.runtime.ConnectInfo) {
|
||||
const messageListener: Array<(message: any) => void> = [];
|
||||
let targetPort: Port;
|
||||
return {
|
||||
setTargetPort(port: Port) {
|
||||
targetPort = port;
|
||||
},
|
||||
messageListener,
|
||||
name: connectInfo?.name || "",
|
||||
sender: {
|
||||
tab: {
|
||||
id: 1,
|
||||
} as unknown as chrome.tabs.Tab,
|
||||
url: window.location.href,
|
||||
},
|
||||
postMessage(message: any) {
|
||||
messageListener.forEach((callback) => {
|
||||
callback(message);
|
||||
});
|
||||
},
|
||||
onMessage: {
|
||||
addListener(callback: (message: any) => void) {
|
||||
targetPort.messageListener.push(callback);
|
||||
},
|
||||
} as unknown as chrome.events.Event<(message: any) => void>,
|
||||
onDisconnect: {
|
||||
addListener() {
|
||||
// do nothing
|
||||
},
|
||||
} as unknown as chrome.events.Event<() => void>,
|
||||
} as unknown as Port;
|
||||
}
|
||||
|
||||
connect(connectInfo?: chrome.runtime.ConnectInfo) {
|
||||
const port = this.Port(connectInfo);
|
||||
const targetPort = this.Port(connectInfo);
|
||||
targetPort.setTargetPort(port);
|
||||
port.setTargetPort(targetPort);
|
||||
this.connectListener.forEach((callback) => {
|
||||
callback(targetPort);
|
||||
});
|
||||
return port;
|
||||
}
|
||||
|
||||
getURL(path: string) {
|
||||
return `${window.location.href}${path}`;
|
||||
}
|
||||
}
|
33
packages/chrome-extension-mock/storage.ts
Normal file
33
packages/chrome-extension-mock/storage.ts
Normal file
@ -0,0 +1,33 @@
|
||||
export default class Storage {
|
||||
sync = new CrhomeStorage();
|
||||
local = new CrhomeStorage();
|
||||
session = new CrhomeStorage();
|
||||
}
|
||||
|
||||
export class CrhomeStorage {
|
||||
data: any = {};
|
||||
|
||||
get(key: string, callback: (data: any) => void) {
|
||||
if (key === null) {
|
||||
callback(this.data);
|
||||
return;
|
||||
}
|
||||
callback({ [key]: this.data[key] });
|
||||
}
|
||||
|
||||
set(data: any, callback: () => void) {
|
||||
this.data = Object.assign(this.data, data);
|
||||
callback();
|
||||
}
|
||||
|
||||
remove(keys: string | string[], callback: () => void) {
|
||||
if (typeof keys === "string") {
|
||||
delete this.data[keys];
|
||||
} else {
|
||||
keys.forEach((key) => {
|
||||
delete this.data[key];
|
||||
});
|
||||
}
|
||||
callback();
|
||||
}
|
||||
}
|
28
packages/chrome-extension-mock/tab.ts
Normal file
28
packages/chrome-extension-mock/tab.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import EventEmitter from "eventemitter3";
|
||||
|
||||
export default class MockTab {
|
||||
hook = new EventEmitter();
|
||||
|
||||
query() {
|
||||
return new Promise((resolve) => {
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
|
||||
create(createProperties: chrome.tabs.CreateProperties, callback?: (tab: chrome.tabs.Tab) => void) {
|
||||
this.hook.emit("create", createProperties);
|
||||
callback?.({
|
||||
id: 1,
|
||||
} as chrome.tabs.Tab);
|
||||
}
|
||||
|
||||
remove(tabId: number) {
|
||||
this.hook.emit("remove", tabId);
|
||||
}
|
||||
|
||||
onRemoved = {
|
||||
addListener: (callback: any) => {
|
||||
this.hook.addListener("remove", callback);
|
||||
},
|
||||
};
|
||||
}
|
59
packages/chrome-extension-mock/web_reqeuest.ts
Normal file
59
packages/chrome-extension-mock/web_reqeuest.ts
Normal file
@ -0,0 +1,59 @@
|
||||
export default class WebRequest {
|
||||
sendHeader?: (
|
||||
details: chrome.webRequest.WebRequestHeadersDetails
|
||||
) => chrome.webRequest.BlockingResponse | void;
|
||||
|
||||
mockXhr(xhr: any): any {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const _this = this;
|
||||
// eslint-disable-next-line func-names
|
||||
return function () {
|
||||
// eslint-disable-next-line new-cap
|
||||
const ret = new xhr();
|
||||
const header: chrome.webRequest.HttpHeader[] = [];
|
||||
ret.setRequestHeader = (k: string, v: string) => {
|
||||
header.push({
|
||||
name: k,
|
||||
value: v,
|
||||
});
|
||||
};
|
||||
const oldSend = ret.send.bind(ret);
|
||||
ret.send = (data: any) => {
|
||||
header.push({
|
||||
name: "cookie",
|
||||
value: "website=example.com",
|
||||
});
|
||||
const resp = _this.sendHeader?.({
|
||||
method: ret.method,
|
||||
url: ret.url,
|
||||
requestHeaders: header,
|
||||
initiator: chrome.runtime.getURL(""),
|
||||
} as chrome.webRequest.WebRequestHeadersDetails) as chrome.webRequest.BlockingResponse;
|
||||
resp.requestHeaders?.forEach((h) => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
ret._authorRequestHeaders!.addHeader(h.name, h.value);
|
||||
});
|
||||
oldSend(data);
|
||||
};
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
onBeforeSendHeaders = {
|
||||
addListener: (callback: any) => {
|
||||
this.sendHeader = callback;
|
||||
},
|
||||
};
|
||||
|
||||
onHeadersReceived = {
|
||||
addListener: () => {
|
||||
// TODO
|
||||
},
|
||||
};
|
||||
|
||||
onCompleted = {
|
||||
addListener: () => {
|
||||
// TODO
|
||||
},
|
||||
};
|
||||
}
|
61
packages/message/mock_message.ts
Normal file
61
packages/message/mock_message.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import EventEmitter from "eventemitter3";
|
||||
import { Message, MessageConnect, MessageSend } from "./server";
|
||||
|
||||
export class MockMessageConnect implements MessageConnect {
|
||||
constructor(protected EE: EventEmitter) {}
|
||||
|
||||
onMessage(callback: (data: any) => void): void {
|
||||
this.EE.on("message", (data: any) => {
|
||||
callback(data);
|
||||
});
|
||||
}
|
||||
|
||||
sendMessage(data: any): void {
|
||||
this.EE.emit("message", data);
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.EE.emit("disconnect");
|
||||
}
|
||||
|
||||
onDisconnect(callback: () => void): void {
|
||||
this.EE.on("disconnect", callback);
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessageSend implements MessageSend {
|
||||
constructor(
|
||||
protected EE: EventEmitter,
|
||||
) {}
|
||||
|
||||
connect(data: any): Promise<MessageConnect> {
|
||||
return new Promise((resolve) => {
|
||||
const EE = new EventEmitter();
|
||||
const con = new MockMessageConnect(EE);
|
||||
this.EE.emit("connect", data, con);
|
||||
resolve(con);
|
||||
});
|
||||
}
|
||||
|
||||
sendMessage(data: any): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
this.EE.emit("message", data, (resp: any) => {
|
||||
resolve(resp);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class MockMessage extends MockMessageSend implements Message {
|
||||
onConnect(callback: (data: any, con: MessageConnect) => void): void {
|
||||
this.EE.on("connect", (data: any, con: MessageConnect) => {
|
||||
callback(data, con);
|
||||
});
|
||||
}
|
||||
|
||||
onMessage(callback: (data: any, sendResponse: (data: any) => void) => void): void {
|
||||
this.EE.on("message", (data: any, sendResponse: (data: any) => void) => {
|
||||
callback(data, sendResponse);
|
||||
});
|
||||
}
|
||||
}
|
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@ -123,9 +123,6 @@ importers:
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 2.1.4
|
||||
version: 2.1.4(vitest@2.1.4(@types/node@22.10.2)(jsdom@25.0.1)(terser@5.36.0))
|
||||
'@webext-core/fake-browser':
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2
|
||||
autoprefixer:
|
||||
specifier: ^10.4.20
|
||||
version: 10.4.20(postcss@8.4.49)
|
||||
@ -153,6 +150,9 @@ importers:
|
||||
jsdom:
|
||||
specifier: ^25.0.1
|
||||
version: 25.0.1
|
||||
mock-xmlhttprequest:
|
||||
specifier: ^8.4.1
|
||||
version: 8.4.1
|
||||
postcss:
|
||||
specifier: ^8.4.49
|
||||
version: 8.4.49
|
||||
@ -1612,9 +1612,6 @@ packages:
|
||||
'@webassemblyjs/wast-printer@1.14.1':
|
||||
resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==}
|
||||
|
||||
'@webext-core/fake-browser@1.3.2':
|
||||
resolution: {integrity: sha512-jFyPWWz+VkHAC9DRIiIPOyu6X/KlC8dYqSKweHz6tsDb86QawtVgZSpYcM+GOQBlZc5DHFo92jJ7cIq4uBnU0A==}
|
||||
|
||||
'@xtuc/ieee754@1.2.0':
|
||||
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
|
||||
|
||||
@ -3165,6 +3162,10 @@ packages:
|
||||
mlly@1.7.3:
|
||||
resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==}
|
||||
|
||||
mock-xmlhttprequest@8.4.1:
|
||||
resolution: {integrity: sha512-2ORxRN+h40+3/Ylw9LKOtYGfQIoX6grGQlmbvMKqaeZ5/l7oeMvqdJxyG/ax3Poy7VbqMTADI6BwTmO7u10Wrw==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
monaco-editor@0.52.2:
|
||||
resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==}
|
||||
|
||||
@ -5946,10 +5947,6 @@ snapshots:
|
||||
'@webassemblyjs/ast': 1.14.1
|
||||
'@xtuc/long': 4.2.2
|
||||
|
||||
'@webext-core/fake-browser@1.3.2':
|
||||
dependencies:
|
||||
lodash.merge: 4.6.2
|
||||
|
||||
'@xtuc/ieee754@1.2.0': {}
|
||||
|
||||
'@xtuc/long@4.2.2': {}
|
||||
@ -7815,6 +7812,8 @@ snapshots:
|
||||
pkg-types: 1.2.1
|
||||
ufo: 1.5.4
|
||||
|
||||
mock-xmlhttprequest@8.4.1: {}
|
||||
|
||||
monaco-editor@0.52.2: {}
|
||||
|
||||
mrmime@1.0.1: {}
|
||||
|
@ -9,7 +9,7 @@ export interface CacheStorage {
|
||||
export class ExtCache implements CacheStorage {
|
||||
get(key: string): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(key, (value) => {
|
||||
chrome.storage.session.get(key, (value) => {
|
||||
resolve(value[key]);
|
||||
});
|
||||
});
|
||||
@ -17,7 +17,7 @@ export class ExtCache implements CacheStorage {
|
||||
|
||||
set(key: string, value: any): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.set(
|
||||
chrome.storage.session.set(
|
||||
{
|
||||
[key]: value,
|
||||
},
|
||||
@ -30,7 +30,7 @@ export class ExtCache implements CacheStorage {
|
||||
|
||||
has(key: string): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(key, (value) => {
|
||||
chrome.storage.session.get(key, (value) => {
|
||||
resolve(value[key] !== undefined);
|
||||
});
|
||||
});
|
||||
@ -38,7 +38,7 @@ export class ExtCache implements CacheStorage {
|
||||
|
||||
del(key: string): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.remove(key, () => {
|
||||
chrome.storage.session.remove(key, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@ -46,7 +46,7 @@ export class ExtCache implements CacheStorage {
|
||||
|
||||
list(): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
chrome.storage.local.get(null, (value) => {
|
||||
chrome.storage.session.get(null, (value) => {
|
||||
resolve(Object.keys(value));
|
||||
});
|
||||
});
|
||||
|
@ -1,62 +0,0 @@
|
||||
import "fake-indexeddb/auto";
|
||||
import { DAO, db } from "./dao";
|
||||
import { LoggerDAO } from "./logger";
|
||||
import migrate from "../migrate";
|
||||
|
||||
migrate();
|
||||
|
||||
interface Test {
|
||||
id: number;
|
||||
data: string;
|
||||
}
|
||||
|
||||
db.version(17).stores({ test: "++id,data" });
|
||||
|
||||
class testDAO extends DAO<Test> {
|
||||
public tableName = "test";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.table = db.table(this.tableName);
|
||||
}
|
||||
}
|
||||
|
||||
describe("dao", () => {
|
||||
const dao = new testDAO();
|
||||
it("测试save", async () => {
|
||||
expect(await dao.save({ id: 0, data: "ok1" })).toEqual(1);
|
||||
|
||||
expect(await dao.save({ id: 0, data: "ok" })).toEqual(2);
|
||||
|
||||
expect(await dao.save({ id: 2, data: "ok2" })).toEqual(2);
|
||||
});
|
||||
|
||||
it("测试find", async () => {
|
||||
expect(await dao.findOne({ id: 1 })).toEqual({ id: 1, data: "ok1" });
|
||||
expect(await dao.findById(2)).toEqual({ id: 2, data: "ok2" });
|
||||
});
|
||||
|
||||
it("测试list", async () => {
|
||||
expect(await dao.list({ id: 1 })).toEqual([{ id: 1, data: "ok1" }]);
|
||||
});
|
||||
|
||||
it("测试delete", async () => {
|
||||
expect(await dao.delete({ id: 1 })).toEqual(1);
|
||||
expect(await dao.findById(1)).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("model", () => {
|
||||
const logger = new LoggerDAO();
|
||||
it("save", async () => {
|
||||
expect(
|
||||
await logger.save({
|
||||
id: 0,
|
||||
level: "info",
|
||||
message: "ok",
|
||||
label: {},
|
||||
createtime: new Date().getTime(),
|
||||
})
|
||||
).toEqual(1);
|
||||
});
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import { Group, MessageConnect } from "@Packages/message/server";
|
||||
|
||||
export class GMApi {
|
||||
export default class GMApi {
|
||||
constructor(private group: Group) {}
|
||||
|
||||
xmlHttpRequest(params: GMSend.XHRDetails, con: MessageConnect | null) {
|
||||
|
@ -6,7 +6,7 @@ import { WindowMessage } from "@Packages/message/window_message";
|
||||
import { ExtensionMessageSend } from "@Packages/message/extension_message";
|
||||
import { ServiceWorkerClient } from "../service_worker/client";
|
||||
import { sendMessage } from "@Packages/message/client";
|
||||
import { GMApi } from "./gm_api";
|
||||
import GMApi from "./gm_api";
|
||||
|
||||
// offscreen环境的管理器
|
||||
export class OffscreenManager {
|
||||
|
@ -1,10 +1,9 @@
|
||||
import LoggerCore from "@App/app/logger/core";
|
||||
import Logger from "@App/app/logger/logger";
|
||||
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
||||
import { Group, MessageConnect, MessageSender } from "@Packages/message/server";
|
||||
import { Group, MessageConnect, MessageSend, MessageSender } from "@Packages/message/server";
|
||||
import { ValueService } from "@App/app/service/service_worker/value";
|
||||
import PermissionVerify from "./permission_verify";
|
||||
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
|
||||
import { connect } from "@Packages/message/client";
|
||||
import Cache, { incr } from "@App/app/cache";
|
||||
import { unsafeHeaders } from "@App/runtime/utils";
|
||||
@ -35,7 +34,7 @@ export default class GMApi {
|
||||
|
||||
constructor(
|
||||
private group: Group,
|
||||
private sender: ServiceWorkerMessageSend,
|
||||
private sender: MessageSend,
|
||||
private value: ValueService
|
||||
) {
|
||||
this.logger = LoggerCore.logger().with({ service: "runtime/gm_api" });
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Server } from "@Packages/message/server";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
import { ScriptService } from "./script";
|
||||
import { ExtensionMessage } from "@Packages/message/extension_message";
|
||||
import { ResourceService } from "./resource";
|
||||
import { ValueService } from "./value";
|
||||
import { RuntimeService } from "./runtime";
|
||||
@ -11,11 +10,11 @@ export type InstallSource = "user" | "system" | "sync" | "subscribe" | "vscode";
|
||||
|
||||
// service worker的管理器
|
||||
export default class ServiceWorkerManager {
|
||||
private api: Server = new Server(new ExtensionMessage());
|
||||
|
||||
private mq: MessageQueue = new MessageQueue(this.api);
|
||||
|
||||
private sender: ServiceWorkerMessageSend = new ServiceWorkerMessageSend();
|
||||
constructor(
|
||||
private api: Server,
|
||||
private mq: MessageQueue,
|
||||
private sender: ServiceWorkerMessageSend
|
||||
) {}
|
||||
|
||||
async initManager() {
|
||||
this.api.on("preparationOffscreen", async () => {
|
||||
@ -32,79 +31,5 @@ export default class ServiceWorkerManager {
|
||||
script.init();
|
||||
const runtime = new RuntimeService(this.api.group("runtime"), this.sender, this.mq, value);
|
||||
runtime.init();
|
||||
|
||||
// 测试xhr
|
||||
// setTimeout(() => {
|
||||
// chrome.tabs.query(
|
||||
// {
|
||||
// url: chrome.runtime.getURL("src/offscreen.html"),
|
||||
// },
|
||||
// (result) => {
|
||||
// console.log(result);
|
||||
// }
|
||||
// );
|
||||
// }, 2000);
|
||||
// group.on("testGmApi", () => {
|
||||
// console.log(chrome.runtime.getURL("src/offscreen.html"));
|
||||
// return new Promise((resolve) => {
|
||||
// chrome.tabs.query({}, (tabs) => {
|
||||
// const excludedTabIds: number[] = [];
|
||||
// tabs.forEach((tab) => {
|
||||
// if (tab.id) {
|
||||
// excludedTabIds.push(tab.id);
|
||||
// }
|
||||
// });
|
||||
// chrome.declarativeNetRequest.updateSessionRules(
|
||||
// {
|
||||
// removeRuleIds: [100],
|
||||
// addRules: [
|
||||
// {
|
||||
// id: 100,
|
||||
// priority: 1,
|
||||
// action: {
|
||||
// type: chrome.declarativeNetRequest.RuleActionType.MODIFY_HEADERS,
|
||||
// requestHeaders: [
|
||||
// {
|
||||
// header: "cookie",
|
||||
// operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
||||
// value: "test=1234314",
|
||||
// },
|
||||
// {
|
||||
// header: "origin",
|
||||
// operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
||||
// value: "https://learn.scriptcat.org",
|
||||
// },
|
||||
// {
|
||||
// header: "user-agent",
|
||||
// operation: chrome.declarativeNetRequest.HeaderOperation.SET,
|
||||
// value: "test",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// condition: {
|
||||
// resourceTypes: [chrome.declarativeNetRequest.ResourceType.XMLHTTPREQUEST],
|
||||
// urlFilter: "^https://scriptcat.org/zh-CN$",
|
||||
// excludedTabIds: excludedTabIds,
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// () => {
|
||||
// resolve(1);
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// chrome.webRequest.onHeadersReceived.addListener(
|
||||
// (details) => {
|
||||
// console.log(details);
|
||||
// },
|
||||
// {
|
||||
// urls: ["<all_urls>"],
|
||||
// types: ["xmlhttprequest"],
|
||||
// },
|
||||
// ["responseHeaders", "extraHeaders"]
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
import { ScriptEnableCallbackValue } from "./client";
|
||||
import { Group } from "@Packages/message/server";
|
||||
import { Group, MessageSend } from "@Packages/message/server";
|
||||
import { Script, SCRIPT_STATUS_ENABLE, SCRIPT_TYPE_NORMAL, ScriptAndCode, ScriptDAO } from "@App/app/repo/scripts";
|
||||
import { ValueService } from "./value";
|
||||
import GMApi from "./gm_api";
|
||||
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
|
||||
|
||||
export class RuntimeService {
|
||||
scriptDAO: ScriptDAO = new ScriptDAO();
|
||||
|
||||
constructor(
|
||||
private group: Group,
|
||||
private sender: ServiceWorkerMessageSend,
|
||||
private sender: MessageSend,
|
||||
private mq: MessageQueue,
|
||||
private value: ValueService
|
||||
) {}
|
||||
|
@ -1,37 +1,27 @@
|
||||
import { formatTime, nextTime, ltever, checkSilenceUpdate } from "./utils";
|
||||
import { describe, test, expect, it } from "vitest";
|
||||
import { nextTime, ltever, checkSilenceUpdate } from "./utils";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
describe("nextTime", () => {
|
||||
test("每分钟表达式", () => {
|
||||
expect(nextTime("* * * * *")).toEqual(
|
||||
dayjs(new Date()).add(1, "minute").format("YYYY-MM-DD HH:mm:00")
|
||||
);
|
||||
expect(nextTime("* * * * *")).toEqual(dayjs(new Date()).add(1, "minute").format("YYYY-MM-DD HH:mm:00"));
|
||||
});
|
||||
test("每分钟一次表达式", () => {
|
||||
expect(nextTime("once * * * *")).toEqual(
|
||||
dayjs(new Date())
|
||||
.add(1, "minute")
|
||||
.format("YYYY-MM-DD HH:mm 每分钟运行一次")
|
||||
dayjs(new Date()).add(1, "minute").format("YYYY-MM-DD HH:mm 每分钟运行一次")
|
||||
);
|
||||
});
|
||||
test("每小时一次表达式", () => {
|
||||
expect(nextTime("* once * * *")).toEqual(
|
||||
dayjs(new Date()).add(1, "hour").format("YYYY-MM-DD HH 每小时运行一次")
|
||||
);
|
||||
expect(nextTime("* once * * *")).toEqual(dayjs(new Date()).add(1, "hour").format("YYYY-MM-DD HH 每小时运行一次"));
|
||||
});
|
||||
test("每天一次表达式", () => {
|
||||
expect(nextTime("* * once * *")).toEqual(
|
||||
dayjs(new Date()).add(1, "day").format("YYYY-MM-DD 每天运行一次")
|
||||
);
|
||||
expect(nextTime("* * once * *")).toEqual(dayjs(new Date()).add(1, "day").format("YYYY-MM-DD 每天运行一次"));
|
||||
});
|
||||
test("每月一次表达式", () => {
|
||||
expect(nextTime("* * * once *")).toEqual(
|
||||
dayjs(new Date()).add(1, "month").format("YYYY-MM 每月运行一次")
|
||||
);
|
||||
expect(nextTime("* * * once *")).toEqual(dayjs(new Date()).add(1, "month").format("YYYY-MM 每月运行一次"));
|
||||
});
|
||||
test("每星期一次表达式", () => {
|
||||
expect(nextTime("* * * * once")).toEqual(
|
||||
dayjs(new Date()).add(1, "week").format("YYYY-MM-DD 每星期运行一次")
|
||||
);
|
||||
expect(nextTime("* * * * once")).toEqual(dayjs(new Date()).add(1, "week").format("YYYY-MM-DD 每星期运行一次"));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Metadata } from "@App/app/repo/scripts";
|
||||
import { CronTime } from "cron";
|
||||
import dayjs from "dayjs";
|
||||
import semver from "semver";
|
||||
@ -186,3 +187,27 @@ export function openInCurrentTab(url: string) {
|
||||
export function isDebug() {
|
||||
return process.env.NODE_ENV === "development";
|
||||
}
|
||||
|
||||
// 检查订阅规则是否改变,是否能够静默更新
|
||||
export function checkSilenceUpdate(oldMeta: Metadata, newMeta: Metadata): boolean {
|
||||
// 判断connect是否改变
|
||||
const oldConnect: { [key: string]: boolean } = {};
|
||||
const newConnect: { [key: string]: boolean } = {};
|
||||
oldMeta.connect &&
|
||||
oldMeta.connect.forEach((val) => {
|
||||
oldConnect[val] = true;
|
||||
});
|
||||
newMeta.connect &&
|
||||
newMeta.connect.forEach((val) => {
|
||||
newConnect[val] = true;
|
||||
});
|
||||
// 老的里面没有新的就需要用户确认了
|
||||
const keys = Object.keys(newConnect);
|
||||
for (let i = 0; i < keys.length; i += 1) {
|
||||
const key = keys[i];
|
||||
if (!oldConnect[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import initTestEnv from "@App/pkg/utils/test_utils";
|
||||
import { ScriptRunResouce } from "@App/app/repo/scripts";
|
||||
import ExecScript from "./exec_script";
|
||||
import { compileScript, compileScriptCode } from "./utils";
|
||||
import { ExtVersion } from "@App/app/const";
|
||||
import initTestEnv from "@Tests/utils";
|
||||
|
||||
initTestEnv();
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { fakeBrowser } from "@webext-core/fake-browser";
|
||||
import { it } from "node:test";
|
||||
import initTestEnv from "@Tests/utils";
|
||||
import { beforeEach, describe, expect } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { initTestEnv } from "@Tests/utils";
|
||||
|
||||
initTestEnv();
|
||||
// serviceWorker环境
|
||||
|
||||
beforeAll(() => {});
|
||||
|
||||
describe("GM xhr", () => {
|
||||
beforeEach(() => {
|
||||
// See https://webext-core.aklinker1.io/fake-browser/reseting-state
|
||||
fakeBrowser.reset();
|
||||
});
|
||||
it("1", async () => {
|
||||
expect(1).toBe(2);
|
||||
it("123123", async () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
});
|
||||
|
@ -3,6 +3,10 @@ import migrate from "./app/migrate";
|
||||
import LoggerCore from "./app/logger/core";
|
||||
import DBWriter from "./app/logger/db_writer";
|
||||
import { LoggerDAO } from "./app/repo/logger";
|
||||
import { ExtensionMessage } from "@Packages/message/extension_message";
|
||||
import { Server } from "@Packages/message/server";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
import { ServiceWorkerMessageSend } from "@Packages/message/window_message";
|
||||
|
||||
const OFFSCREEN_DOCUMENT_PATH = "src/offscreen.html";
|
||||
|
||||
@ -51,7 +55,8 @@ async function main() {
|
||||
});
|
||||
loggerCore.logger().debug("service worker start");
|
||||
// 初始化管理器
|
||||
const manager = new ServiceWorkerManager();
|
||||
const server = new Server(new ExtensionMessage());
|
||||
const manager = new ServiceWorkerManager(server, new MessageQueue(server), new ServiceWorkerMessageSend());
|
||||
manager.initManager();
|
||||
// 初始化沙盒环境
|
||||
await setupOffscreenDocument();
|
||||
|
@ -1,2 +0,0 @@
|
||||
// <root>/__mocks__/webextension-polyfill.ts
|
||||
export { fakeBrowser as default } from "@webext-core/fake-browser";
|
55
tests/utils.test.ts
Normal file
55
tests/utils.test.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { describe, expect, it, vitest } from "vitest";
|
||||
import { initTestEnv, initTestGMApi } from "./utils";
|
||||
import GMApi from "@App/runtime/content/gm_api";
|
||||
import { randomUUID } from "crypto";
|
||||
import { newMockXhr } from "mock-xmlhttprequest";
|
||||
import { Script, ScriptDAO } from "@App/app/repo/scripts";
|
||||
|
||||
initTestEnv();
|
||||
|
||||
describe("测试GMApi环境", async () => {
|
||||
const msg = initTestGMApi();
|
||||
const script: Script = {
|
||||
uuid: randomUUID(),
|
||||
name: "test",
|
||||
metadata: {
|
||||
grant: [
|
||||
// gm xhr
|
||||
"GM_xmlhttpRequest",
|
||||
],
|
||||
connect: ["example.com"],
|
||||
},
|
||||
namespace: "",
|
||||
type: 1,
|
||||
status: 1,
|
||||
sort: 0,
|
||||
runStatus: "running",
|
||||
createtime: 0,
|
||||
checktime: 0,
|
||||
};
|
||||
await new ScriptDAO().save(script);
|
||||
const gmApi = new GMApi(msg);
|
||||
//@ts-ignore
|
||||
gmApi.scriptRes = {
|
||||
uuid: script.uuid,
|
||||
};
|
||||
const mockXhr = newMockXhr();
|
||||
mockXhr.onSend = async (request) => {
|
||||
return request.respond(200, {}, "example");
|
||||
};
|
||||
global.XMLHttpRequest = mockXhr;
|
||||
it("test GM xhr", async () => {
|
||||
const onload = vitest.fn();
|
||||
await new Promise((resolve) => {
|
||||
gmApi.GM_xmlhttpRequest({
|
||||
url: "https://scriptcat.org/zh-CN",
|
||||
onload: (res) => {
|
||||
console.log(res);
|
||||
resolve(res);
|
||||
onload(res);
|
||||
},
|
||||
});
|
||||
});
|
||||
expect(onload).toBeCalled();
|
||||
});
|
||||
});
|
@ -3,8 +3,15 @@ import LoggerCore from "@App/app/logger/core";
|
||||
import DBWriter from "@App/app/logger/db_writer";
|
||||
import migrate from "@App/app/migrate";
|
||||
import { LoggerDAO } from "@App/app/repo/logger";
|
||||
import { MockMessage } from "@Packages/message/mock_message";
|
||||
import { Message, Server } from "@Packages/message/server";
|
||||
import { MessageQueue } from "@Packages/message/message_queue";
|
||||
import { ValueService } from "@App/app/service/service_worker/value";
|
||||
import GMApi from "@App/app/service/service_worker/gm_api";
|
||||
import OffscreenGMApi from "@App/app/service/offscreen/gm_api";
|
||||
import EventEmitter from "eventemitter3";
|
||||
|
||||
export default function initTestEnv() {
|
||||
export function initTestEnv() {
|
||||
// @ts-ignore
|
||||
if (global.initTest) {
|
||||
return;
|
||||
@ -42,3 +49,27 @@ export default function initTestEnv() {
|
||||
});
|
||||
logger.logger().debug("test start");
|
||||
}
|
||||
|
||||
export function initTestGMApi(): Message {
|
||||
const wsEE = new EventEmitter();
|
||||
const wsMessage = new MockMessage(wsEE);
|
||||
const osEE = new EventEmitter();
|
||||
const osMessage = new MockMessage(osEE);
|
||||
|
||||
const serviceWorkerServer = new Server(wsMessage);
|
||||
const mq = new MessageQueue(serviceWorkerServer);
|
||||
const valueService = new ValueService(serviceWorkerServer.group("value"), mq);
|
||||
const swGMApi = new GMApi(serviceWorkerServer.group("runtime"), osMessage, valueService);
|
||||
|
||||
valueService.init();
|
||||
swGMApi.start();
|
||||
|
||||
// offscreen
|
||||
const offscreenServer = new Server(osMessage);
|
||||
const osGMApi = new OffscreenGMApi(offscreenServer.group("gmApi"));
|
||||
osGMApi.init();
|
||||
|
||||
return wsMessage;
|
||||
}
|
||||
|
||||
export function initTestOffscreen() {}
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { vi } from "vitest";
|
||||
import chromeMock from "@Packages/chrome-extension-mock";
|
||||
|
||||
vi.mock("webextension-polyfill");
|
||||
chromeMock.init();
|
||||
|
@ -10,6 +10,7 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
// List setup file
|
||||
setupFiles: ["./tests/vitest.setup.ts"],
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user