aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstonedDiscord <Tukz@gmx.de>2026-01-01 16:56:51 +0100
committerGitHub <noreply@github.com>2026-01-01 16:56:51 +0100
commit33744e44ceaf3825da451ba38281fd84b255de80 (patch)
treee6c080d7e8e6f56652c2269a380eff40b899a065
parente07fe372bc20d16eb590b68ed256007312b3801a (diff)
parent01ecb948edb015613e05bc2b6da4021137957745 (diff)
Merge pull request #286 from AttorneyOnline/bnnuy
Bnnuy
-rw-r--r--.github/workflows/deploy.yml11
-rw-r--r--.github/workflows/test.yml8
-rw-r--r--bunfig.toml2
-rw-r--r--happydom.ts3
-rw-r--r--package.json57
-rw-r--r--webAO/__tests__/aoml.test.ts67
-rw-r--r--webAO/__tests__/isLowMemory.test.ts22
-rw-r--r--webAO/__tests__/setEmote.test.js140
-rw-r--r--webAO/utils/aoml.ts22
-rw-r--r--webAO/viewport/constants/defaultChatMsg.ts4
-rw-r--r--webAO/viewport/utils/handleICSpeaking.ts15
11 files changed, 111 insertions, 240 deletions
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 296323c..1321bdc 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -12,16 +12,15 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
- uses: actions/setup-node@v6
+ uses: oven-sh/setup-bun@v2
with:
- node-version: 20
- cache: 'npm'
+ bun-version: latest
- name: Install Dependencies
- run: npm install
+ run: bun install
- name: Build Project
- run: npm run build
+ run: bun run build
- name: Run Tests
- run: npm test
+ run: bun test
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@v4.7.6
with:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5a1a8e7..e7d8e6d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -9,10 +9,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
- uses: actions/setup-node@v6
+ uses: oven-sh/setup-bun@v2
with:
- node-version: 20
+ bun-version: 1.3.5
- name: Install Dependencies
- run: npm install
+ run: bun install
- name: Run Tests
- run: npm test
+ run: bun test
diff --git a/bunfig.toml b/bunfig.toml
new file mode 100644
index 0000000..d20eff3
--- /dev/null
+++ b/bunfig.toml
@@ -0,0 +1,2 @@
+[test]
+preload = "./happydom.ts"
diff --git a/happydom.ts b/happydom.ts
new file mode 100644
index 0000000..7f712d0
--- /dev/null
+++ b/happydom.ts
@@ -0,0 +1,3 @@
+import { GlobalRegistrator } from "@happy-dom/global-registrator";
+
+GlobalRegistrator.register();
diff --git a/package.json b/package.json
index 4cc9d73..92b4549 100644
--- a/package.json
+++ b/package.json
@@ -25,40 +25,41 @@
},
"homepage": "https://github.com/AttorneyOnline/webAO#readme",
"devDependencies": {
- "@babel/core": "^7.27.7",
- "@babel/plugin-transform-modules-commonjs": "^7.16.8",
- "@babel/preset-env": "^7.27.2",
- "@babel/preset-typescript": "^7.27.1",
- "@types/jest": "^29.4.0",
- "@types/node": "^18.0.0",
- "@typescript-eslint/eslint-plugin": "^6.7.2",
- "@typescript-eslint/parser": "^6.7.2",
- "babel-jest": "^30.0.2",
- "babel-loader": "^9.1.2",
+ "@babel/core": "^7.28.5",
+ "@babel/plugin-transform-modules-commonjs": "^7.27.1",
+ "@babel/preset-env": "^7.28.5",
+ "@babel/preset-typescript": "^7.28.5",
+ "@happy-dom/global-registrator": "^20.0.11",
+ "@types/jest": "^29.5.14",
+ "@types/node": "^18.19.130",
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
+ "@typescript-eslint/parser": "^6.21.0",
+ "babel-jest": "^30.2.0",
+ "babel-loader": "^9.2.1",
"copy-webpack-plugin": "^11.0.0",
- "dotenv": "^16.0.0",
- "eslint": "^8.49.0",
+ "dotenv": "^16.6.1",
+ "eslint": "^8.57.1",
"eslint-config-airbnb-base": "^15.0.0",
- "eslint-plugin-import": "^2.25.4",
- "glob": "^7.2.0",
- "html-webpack-plugin": "^5.5.0",
+ "eslint-plugin-import": "^2.32.0",
+ "glob": "^7.2.3",
+ "html-webpack-plugin": "^5.6.5",
"husky": "^8.0.3",
- "jest": "^29.4.3",
- "typescript": "^5.2.2",
- "webpack": "^5.94.0",
+ "jest": "^29.7.0",
+ "typescript": "^5.9.3",
+ "webpack": "^5.104.1",
"webpack-cli": "^5.1.4",
- "webpack-dev-server": "^5.2.1",
- "workbox-webpack-plugin": "^7.3.0"
+ "webpack-dev-server": "^5.2.2",
+ "workbox-webpack-plugin": "^7.4.0"
},
"dependencies": {
- "@fingerprintjs/fingerprintjs": "^3.4.0",
- "@types/websocket": "^1.0.5",
- "core-js": "^3.21.1",
- "golden-layout": "^2.5.0",
- "jest-environment-jsdom": "^29.5.0",
- "regenerator-runtime": "^0.13.9",
- "source-map-loader": "^4.0.1",
- "ts-loader": "^9.2.8"
+ "@fingerprintjs/fingerprintjs": "^3.4.2",
+ "@types/websocket": "^1.0.10",
+ "core-js": "^3.47.0",
+ "golden-layout": "^2.6.0",
+ "jest-environment-jsdom": "^29.7.0",
+ "regenerator-runtime": "^0.13.11",
+ "source-map-loader": "^4.0.2",
+ "ts-loader": "^9.5.4"
},
"browserslist": [
"defaults",
diff --git a/webAO/__tests__/aoml.test.ts b/webAO/__tests__/aoml.test.ts
index b799da6..d612945 100644
--- a/webAO/__tests__/aoml.test.ts
+++ b/webAO/__tests__/aoml.test.ts
@@ -1,8 +1,4 @@
-import request from "../services/request";
-import mlConfig from "../utils/aoml";
-
-jest.mock("../services/request");
-const networkRequest = `
+const configIni = `
c0 = 247, 247, 247
c0_name = White
c0_talking = 1
@@ -36,73 +32,56 @@ c6_remove = 0
c6_talking = 0
`;
-const mockRequest = request as jest.MockedFunction<typeof request>;
-mockRequest.mockReturnValue(Promise.resolve(networkRequest));
-
-describe("mlConfig", () => {
- beforeEach(() => {
- // Clear all instances and calls to constructor and all methods:
- mockRequest.mockClear();
- });
+import mlConfig from "../utils/aoml";
- it("Should make a network request", () => {
- mlConfig("localhost");
- expect(mockRequest).toHaveBeenCalledTimes(1);
- });
-});
describe("applyMarkdown", () => {
- const config = mlConfig("localhost");
-
- beforeEach(() => {
- // Clear all instances and calls to constructor and all methods:
- mockRequest.mockClear();
- });
+ const config = mlConfig(configIni);
- it("Should create an array of spans containing letters", async () => {
+ it("Should create an array of spans containing letters", () => {
const word = `hello`;
- const actual = await config.applyMarkdown(`hello`, `blue`);
+ const actual = config.applyMarkdown(`hello`, `blue`);
let index = 0;
for (const element of actual) {
expect(element.innerHTML).toBe(word[index]);
index++;
}
});
- it("Should add colors based on settings", async () => {
- const config = mlConfig("localhost");
- const actual = await config.applyMarkdown(`(heya)`, `blue`);
+ it("Should add colors based on settings", () => {
+ const config = mlConfig(configIni);
+ const actual = config.applyMarkdown(`(heya)`, `blue`);
expect(actual[0].getAttribute("style")).toBe("color: rgb(107, 198, 247);");
});
- it("Should keep a letter if remove = 0", async () => {
- const config = mlConfig("localhost");
+ it("Should keep a letter if remove = 0", () => {
+ const config = mlConfig(configIni);
- const actual = await config.applyMarkdown(`(What())Hey!`, `white`);
+ const actual = config.applyMarkdown(`(What())Hey!`, `white`);
const expected = `(`;
expect(actual[5].innerHTML).toBe(expected);
});
- it("Should remove a letter if remove = 1", async () => {
- const config = mlConfig("localhost");
+ it("Should remove a letter if remove = 1", () => {
+ const config = mlConfig(configIni);
- const actual = await config.applyMarkdown(`~What~()Hey!`, `white`);
+ const actual = config.applyMarkdown(`~What~()Hey!`, `white`);
const expected = ``;
expect(actual[0].innerHTML).toBe(expected);
});
- it("Should remove a letter if remove = 1", async () => {
- const config = mlConfig("localhost");
+ it("Should remove a letter if remove = 1", () => {
+ const config = mlConfig(configIni);
- const actual = await config.applyMarkdown(`~What~()Hey!`, `white`);
+ const actual = config.applyMarkdown(`~What~()Hey!`, `white`);
const expected = ``;
expect(actual[0].innerHTML).toBe(expected);
});
- it("Should keep a closing letter if remove = 0", async () => {
- const config = mlConfig("localhost");
+ it("Should keep a closing letter if remove = 0", () => {
+ const config = mlConfig(configIni);
- const actual = await config.applyMarkdown(`~NO[]~!`, `white`);
+ const actual = config.applyMarkdown(`~NO[]~!`, `white`);
const expected = ``;
expect(actual[4].innerHTML).toBe(expected);
});
- it("Should remove a closing letter if remove = 1", async () => {
- const config = mlConfig("localhost");
- const actual = await config.applyMarkdown(`~NO||~!`, `white`);
+ it("Should remove a closing letter if remove = 1", () => {
+ const config = mlConfig(configIni);
+ const actual = config.applyMarkdown(`~NO||~!`, `white`);
const expected = ``;
expect(actual[5].innerHTML).toBe(expected);
});
diff --git a/webAO/__tests__/isLowMemory.test.ts b/webAO/__tests__/isLowMemory.test.ts
index 6428e17..870b40e 100644
--- a/webAO/__tests__/isLowMemory.test.ts
+++ b/webAO/__tests__/isLowMemory.test.ts
@@ -1,11 +1,31 @@
import { isLowMemory } from '../client/isLowMemory';
import { setOldLoading } from '../client';
+import { AO_HOST, setAOhost } from '../client/aoHost';
-// Mock the setOldLoading function
+// Mock the setOldLoading function and prevent any network requests
jest.mock('../client', () => ({
setOldLoading: jest.fn(),
}));
+// Mock any potential network requests
+jest.mock('../services/request', () => ({
+ __esModule: true,
+ default: jest.fn().mockResolvedValue(''),
+ request: jest.fn().mockResolvedValue(''),
+ requestBuffer: jest.fn().mockResolvedValue(new ArrayBuffer(0)),
+}));
+
+// Mock the fileExists function to prevent network requests
+jest.mock('../utils/fileExists', () => ({
+ __esModule: true,
+ default: jest.fn().mockResolvedValue(false),
+}));
+
+// Set AO_HOST to a valid URL before tests run
+beforeAll(() => {
+ setAOhost('https://example.com/');
+});
+
describe('isLowMemory', () => {
beforeEach(() => {
// Reset mock before each test to ensure isolation
diff --git a/webAO/__tests__/setEmote.test.js b/webAO/__tests__/setEmote.test.js
deleted file mode 100644
index 3a4a521..0000000
--- a/webAO/__tests__/setEmote.test.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import setEmote from "../client/setEmote.ts";
-import Client from "../client.ts";
-import fileExists from "../utils/fileExists.ts";
-import transparentPng from "../constants/transparentPng.js";
-
-jest.mock("../viewport/utils/createMusic");
-jest.mock("../utils/fileExists");
-jest.mock("../viewport/utils/createSfxAudio");
-jest.mock("../viewport/utils/createShoutAudio");
-jest.mock("../viewport/utils/createTestimonyAudio");
-describe("setEmote", () => {
- const AO_HOST = "";
-
- const client = new Client("127.0.0.1");
- const firstExtension = ".gif";
-
- test("Should have a client_def_char_img with a valid source", async () => {
- fileExists.mockReturnValue(true);
- document.body.innerHTML = `
- <img id="client_def_char_img" />
- `;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 0, "def");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
- expect(document.getElementById("client_def_char_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_pro_char_img to have a valid src", async () => {
- document.body.innerHTML = `
- <img id="client_pro_char_img" />
-
- `;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 0, "pro");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
- expect(document.getElementById("client_pro_char_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_wit_char_img", async () => {
- document.body.innerHTML = `
- <img id="client_wit_char_img" />
- `;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 0, "wit");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_wit_char_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_def_pair_img", async () => {
- document.body.innerHTML = `
-<img id="client_def_pair_img" />
-
-`;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 1, "def");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_def_pair_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_pro_pair_img", async () => {
- document.body.innerHTML = `
-<img id="client_pro_pair_img" />
-
-`;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 1, "pro");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_pro_pair_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_wit_pair_img", async () => {
- document.body.innerHTML = `
-<img id="client_wit_pair_img" />
-
-`;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 1, "wit");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_wit_pair_img").src).toEqual(
- expected,
- );
- });
- test("Should have a client_char_img", async () => {
- document.body.innerHTML = `
- <img id="client_char_img" />
-
- `;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 0, "notvalid");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_char_img").src).toEqual(expected);
- });
- test("Should have a client_pair_img", async () => {
- document.body.innerHTML = `
- <img id="client_pair_img" />
- `;
- await setEmote(AO_HOST, client, "salanto", "coding", "(a)", 1, "notvalid");
- const expected = `http://localhost/characters/salanto/(a)coding${firstExtension}`;
-
- expect(document.getElementById("client_pair_img").src).toEqual(expected);
- });
- test("Should handle .png urls differently", async () => {
- fileExists.mockReturnValueOnce(false);
- document.body.innerHTML = `
- <img id="client_pair_img" />
- `;
- await setEmote(
- AO_HOST,
- client,
- "salanto",
- "coding",
- "prefixNotValid",
- 1,
- "notvalid",
- );
- const expected = "http://localhost/characters/salanto/coding.png";
-
- expect(document.getElementById("client_pair_img").src).toEqual(expected);
- });
- test("Should replace character if new character responds", async () => {
- fileExists.mockReturnValue(false);
- document.body.innerHTML = `
- <img id="client_pair_img" />
- `;
- await setEmote(
- AO_HOST,
- client,
- "salanto",
- "coding",
- "prefixNotValid",
- 1,
- "notvalid",
- );
- const expected = transparentPng;
- expect(document.getElementById("client_pair_img").src).toEqual(expected);
- });
-});
diff --git a/webAO/utils/aoml.ts b/webAO/utils/aoml.ts
index f4a6da5..7611399 100644
--- a/webAO/utils/aoml.ts
+++ b/webAO/utils/aoml.ts
@@ -1,4 +1,3 @@
-import request from "../services/request";
interface Aoml {
[key: string]: string | number;
@@ -33,15 +32,12 @@ const aomlParser = (text: string) => {
return parsed;
};
-const mlConfig = (AO_HOST: string) => {
- const defaultUrl = `${AO_HOST}themes/default/chat_config.ini`;
- const aomlParsed: Promise<{ [key: string]: Aoml }> = request(defaultUrl).then(
- (data) => aomlParser(data),
- );
+const mlConfig = (iniContent: string) => {
+ const aomlParsed: { [key: string]: Aoml } = aomlParser(iniContent);
- const createIdentifiers = async () => {
+ const createIdentifiers = () => {
const identifiers = new Map<string, Aoml>();
- for (const [ruleName, value] of Object.entries(await aomlParsed)) {
+ for (const [ruleName, value] of Object.entries(aomlParsed)) {
if (value.start && value.end) {
identifiers.set(value.start, value);
identifiers.set(value.end, value);
@@ -49,18 +45,18 @@ const mlConfig = (AO_HOST: string) => {
}
return identifiers;
};
- const createStartIdentifiers = async () => {
+ const createStartIdentifiers = () => {
const startingIdentifiers = new Set<string>();
- for (const [ruleName, value] of Object.entries(await aomlParsed)) {
+ for (const [ruleName, value] of Object.entries(aomlParsed)) {
if (value?.start && value?.end) {
startingIdentifiers.add(value.start);
}
}
return startingIdentifiers;
};
- const applyMarkdown = async (text: string, defaultColor: string) => {
- const identifiers = await createIdentifiers();
- const startIdentifiers = await createStartIdentifiers();
+ const applyMarkdown = (text: string, defaultColor: string) => {
+ const identifiers = createIdentifiers();
+ const startIdentifiers = createStartIdentifiers();
const closingStack = [];
const colorStack = [];
// each value in output will be an html element
diff --git a/webAO/viewport/constants/defaultChatMsg.ts b/webAO/viewport/constants/defaultChatMsg.ts
index fa25b33..67c60b8 100644
--- a/webAO/viewport/constants/defaultChatMsg.ts
+++ b/webAO/viewport/constants/defaultChatMsg.ts
@@ -1,6 +1,8 @@
-import { UPDATE_INTERVAL } from "../../client";
import { ChatMsg } from "../interfaces/ChatMsg";
+// Define UPDATE_INTERVAL locally to avoid circular dependency
+const UPDATE_INTERVAL = 60;
+
export const defaultChatMsg = {
content: "",
objection: 0,
diff --git a/webAO/viewport/utils/handleICSpeaking.ts b/webAO/viewport/utils/handleICSpeaking.ts
index b037e69..c0a9ae1 100644
--- a/webAO/viewport/utils/handleICSpeaking.ts
+++ b/webAO/viewport/utils/handleICSpeaking.ts
@@ -11,8 +11,17 @@ import { resizeChatbox } from "../../dom/resizeChatbox";
import transparentPng from "../../constants/transparentPng";
import { COLORS } from "../constants/colors";
import mlConfig from "../../utils/aoml";
+import request from "../../services/request";
-const attorneyMarkdown = mlConfig(AO_HOST);
+let attorneyMarkdown: ReturnType<typeof mlConfig> | null = null;
+
+const initAttorneyMarkdown = async () => {
+ if (!attorneyMarkdown) {
+ const iniContent = await request(`${AO_HOST}themes/default/chat_config.ini`);
+ attorneyMarkdown = mlConfig(iniContent);
+ }
+ return attorneyMarkdown;
+};
export let startFirstTickCheck: boolean;
export const setStartFirstTickCheck = (val: boolean) => {
@@ -337,9 +346,9 @@ export const handle_ic_speaking = async (playerChatMsg: ChatMsg) => {
}
try {
- client.viewport.getChatmsg().parsed = await attorneyMarkdown.applyMarkdown(
+ const markdown = await initAttorneyMarkdown();
+ client.viewport.getChatmsg().parsed = markdown.applyMarkdown(
client.viewport.getChatmsg().content,
-
COLORS[client.viewport.getChatmsg().color],
);
} catch (error) {