Recently Written · git

bd-plugin-profiler

BetterDiscord plugin that measures the main-thread time of each plugin

git clone https://github.com/equwal/bd-plugin-profiler

Log | Files | Refs


test/lifecycle.test.js (3633 bytes)

1 "use strict";
2 const test = require("node:test");
3 const assert = require("node:assert/strict");
4 const {loadBdPatcher} = require("./helpers/installed.js");
5 const PluginProfiler = require("../00PluginProfiler.plugin.js");
6 
7 const META = {filename: "00PluginProfiler.plugin.js", name: "PluginProfiler"};
8 
9 function fakeWindow() {
10     return {
11         setTimeout: () => 1,
12         setInterval: () => 2,
13         MutationObserver: class {},
14         ResizeObserver: class {},
15         IntersectionObserver: class {}
16     };
17 }
18 
19 test.afterEach(() => {
20     delete globalThis.window;
21     delete globalThis.BdApi;
22 });
23 
24 test("long-frame labels show script files and plugin names, but never the page URL", () => {
25     const plugin = new PluginProfiler(META);
26     plugin.fileOwners = new Map([["Foo.plugin.js", "Foo"]]);
27     const page = "https://discord.com/channels/@me/123456789012345678";
28     assert.equal(plugin.scriptLabel({sourceURL: page, invoker: page, invokerType: "classic-script"}), "page");
29     assert.equal(plugin.scriptLabel({sourceURL: page, invoker: "DIV.onclick"}), "page (DIV.onclick)");
30     assert.equal(plugin.scriptLabel({sourceURL: "https://discord.com/assets/8fe5.js", invoker: "https://discord.com/assets/8fe5.js"}), "8fe5.js");
31     assert.equal(plugin.scriptLabel({sourceURL: "https://discord.com/assets/5567.js?v=1", sourceFunctionName: "r"}), "5567.js (r)");
32     assert.equal(plugin.scriptLabel({sourceURL: "betterdiscord://betterdiscord/plugins/Foo.plugin.js", invoker: "Window.setTimeout"}), "Foo (Window.setTimeout)");
33 });
34 
35 test("a failed start removes every hook", () => {
36     const scope = globalThis.window = fakeWindow();
37     const originals = {...scope};
38     globalThis.BdApi = {
39         Plugins: {getAll() { throw new Error("broken API"); }},
40         Patcher: {getPatchesByCaller: () => []}
41     };
42     const plugin = new PluginProfiler(META);
43     assert.throws(() => plugin.start(), /broken API/);
44     for (const key of Object.keys(originals)) assert.equal(scope[key], originals[key], key);
45     assert.equal(plugin.recorder, null);
46 });
47 
48 test("start wraps patches and hooks of other plugins, and stop removes all of them", () => {
49     const Patcher = loadBdPatcher({err() {}});
50     const target = {work() { return 1; }};
51     const callback = () => 2;
52     Patcher.after("Foo", target, "work", callback);
53     const observer = () => {};
54     const instance = {observer, start() {}, stop() {}};
55     const scope = globalThis.window = fakeWindow();
56     const originals = {...scope};
57     globalThis.BdApi = {
58         Plugins: {getAll: () => [{name: "Foo", id: "Foo", filename: "Foo.plugin.js", instance}, {...META, id: META.name, instance: {}}]},
59         Patcher: {getPatchesByCaller: caller => Patcher.getPatchesByCaller(caller)}
60     };
61 
62     const plugin = new PluginProfiler(META);
63     plugin.start();
64     try {
65         assert.notEqual(Patcher.getPatchesByCaller("Foo")[0].callback, callback);
66         assert.notEqual(instance.observer, observer);
67         assert.notEqual(scope.setTimeout, originals.setTimeout);
68         assert.equal(target.work(), 2);
69         instance.observer();
70         const row = plugin.snapshot().rows.find(r => r.name === "Foo");
71         assert.equal(row.calls, 2);
72         assert.match(plugin.report(), /^PluginProfiler 1\.0\.0 report/);
73         assert.match(plugin.report(), /\nFoo /);
74     }
75     finally {
76         plugin.stop();
77     }
78     assert.equal(Patcher.getPatchesByCaller("Foo")[0].callback, callback);
79     assert.equal(instance.observer, observer);
80     for (const key of Object.keys(originals)) assert.equal(scope[key], originals[key], key);
81     assert.equal(plugin.snapshot(), null);
82 });