diff --git a/bin/version.ts b/bin/version.ts index 27e4a617..8758a000 100644 --- a/bin/version.ts +++ b/bin/version.ts @@ -1,7 +1,7 @@ -import { $ } from "bun"; +import { execa } from "execa"; import * as fs from "node:fs/promises"; import * as path from "node:path"; -import * as p from "../package.json"; +import p from "../package.json" assert { type: "json" }; export type VersionInfo = { hash: string; @@ -14,14 +14,29 @@ export type VersionInfo = { export async function getVersionInfo(): Promise { const d = new Date(); const date = `${d.getDate()} ${d.toLocaleString("default", { month: "short" })} ${d.getFullYear()} ${d.getHours()}:${String(d.getMinutes()).padStart(2, "0")}`; + + const getHash = async () => { + try { + const { stdout } = await execa("git", ["rev-parse", "--short", "HEAD"]); + return stdout.trim(); + } catch { + return "unknown"; + } + }; + + const getStatus = async () => { + try { + const { stdout } = await execa("git", ["status", "-s"]); + return stdout.replace(/\n$/, ""); + } catch { + return ""; + } + }; + return { - hash: - process.env.VITE_NPA_COMMIT_HASH ?? - (await $`git rev-parse --short HEAD`.text()).trim(), + hash: process.env.VITE_NPA_COMMIT_HASH ?? (await getHash()), date: process.env.VITE_NPA_VERSION_DATE ?? date, - status: - process.env.VITE_NPA_GIT_STATUS ?? - (await $`git status -s`.text()).replace(/\n$/, ""), + status: process.env.VITE_NPA_GIT_STATUS ?? (await getStatus()), version: process.env.VITE_NPA_VERSION ?? p.version, display: process.env.VITE_NPA_VERSION_STRING ?? "", }; diff --git a/package-lock.json b/package-lock.json index 840b51c1..a6937ada 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@biomejs/biome": "^1.8.2", - "@playwright/test": "^1.55.0", + "@playwright/test": "^1.59.1", "@types/bun": "^1.1.10", "@types/chai": "^4.3.3", "@types/chrome": "^0.0.193", @@ -29,7 +29,7 @@ "execa": "^6.1.0", "husky": "^8.0.0", "jsdom": "^22.0.0", - "playwright": "^1.55.0", + "playwright": "^1.59.1", "ts-loader": "^9.4.2", "tsx": "^4.20.5", "typescript": "^4.9.4", @@ -1355,13 +1355,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", - "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.55.0" + "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -3738,13 +3738,13 @@ } }, "node_modules/playwright": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", - "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.55.0" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -3757,9 +3757,9 @@ } }, "node_modules/playwright-core": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", - "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6871,12 +6871,12 @@ } }, "@playwright/test": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", - "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, "requires": { - "playwright": "1.55.0" + "playwright": "1.59.1" } }, "@protobufjs/aspromise": { @@ -8715,13 +8715,13 @@ } }, "playwright": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", - "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.55.0" + "playwright-core": "1.59.1" }, "dependencies": { "fsevents": { @@ -8734,9 +8734,9 @@ } }, "playwright-core": { - "version": "1.55.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", - "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true }, "postcss": { diff --git a/package.json b/package.json index 67dbd317..314ea6e8 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,10 @@ "type": "module", "description": "Chrome extension for Neptune's Pride players.", "scripts": { - "start": "bun esbuild.dev.ts", - "build": "bun esbuild.npa.ts", + "start": "npx tsx esbuild.dev.ts", + "build": "npx tsx esbuild.npa.ts", "docs:book": "node scripts/build-npa-book.mjs", - "npa": "bun esbuild.npa.ts", + "npa": "npx tsx esbuild.npa.ts", "lint": "bunx biome check src", "lint:fix": "bunx biome check --write src", "format": "bunx biome format --write src tests", @@ -25,7 +25,7 @@ "license": "GPLv3", "devDependencies": { "@biomejs/biome": "^1.8.2", - "@playwright/test": "^1.55.0", + "@playwright/test": "^1.59.1", "@types/bun": "^1.1.10", "@types/chai": "^4.3.3", "@types/chrome": "^0.0.193", @@ -39,7 +39,7 @@ "execa": "^6.1.0", "husky": "^8.0.0", "jsdom": "^22.0.0", - "playwright": "^1.55.0", + "playwright": "^1.59.1", "ts-loader": "^9.4.2", "tsx": "^4.20.5", "typescript": "^4.9.4", diff --git a/scripts/setup-tests.sh b/scripts/setup-tests.sh index 8498a301..c795c247 100755 --- a/scripts/setup-tests.sh +++ b/scripts/setup-tests.sh @@ -39,15 +39,15 @@ if [ ! -d "dist" ] || [ ! -f "dist/intel.js" ]; then fi # Install Playwright browsers if not already installed -if ! bunx playwright --version >/dev/null 2>&1; then +if ! npx playwright --version >/dev/null 2>&1; then echo "Installing Playwright..." npm install --save-dev playwright @playwright/test fi echo "Checking Playwright browser installation..." -if ! bunx playwright list-files chromium >/dev/null 2>&1; then +if ! npx playwright list-files chromium >/dev/null 2>&1; then echo "Installing Playwright browsers..." - bunx playwright install chromium || echo "WARNING: Playwright browser installation failed. Tests may not work." + npx playwright install chromium || echo "WARNING: Playwright browser installation failed. Tests may not work." else echo "Playwright browsers already installed." fi diff --git a/src/intel.ts b/src/intel.ts index bbb61dc7..b6a9bb63 100644 --- a/src/intel.ts +++ b/src/intel.ts @@ -4061,7 +4061,7 @@ async function NeptunesPrideAgent() { }; onTrigger("paste_report", reportPasteHook); npui.NewMessageCommentBox = () => { - const widget = superNewMessageCommentBox(); + const widget = new (superNewMessageCommentBox as any)(); const reportButton = new UI.Button( "npa_paste", "paste_report", diff --git a/tests/playwright/008-messaging-support/008-messaging-support.spec.ts b/tests/playwright/008-messaging-support/008-messaging-support.spec.ts new file mode 100644 index 00000000..b762122b --- /dev/null +++ b/tests/playwright/008-messaging-support/008-messaging-support.spec.ts @@ -0,0 +1,165 @@ +import { expect, test } from "../fixtures"; +import { waitForAgentHooks } from "../helpers"; +import { TestStepHelper, waitForAnimations } from "../support/test-step-helper"; + +test("documents the messaging support and helpers", async ({ appPage }, testInfo) => { + const helper = new TestStepHelper(appPage, testInfo); + + // Increase viewport size to ensure all UI elements are visible + await appPage.setViewportSize({ width: 1600, height: 1200 }); + + helper.setMetadata({ + title: "Messaging Support Validation", + validationGoal: "Verify the message composition workflow, NPA messaging helpers (Intel/Screenshot buttons), and autocomplete functionality.", + docsTitle: "Messaging Support", + docsSummary: "NPA enhances the messaging experience with automated report insertion, screenshot sharing, and intelligent autocomplete for player and star names.", + bookSection: "Messaging support", + }); + + await waitForAgentHooks(appPage); + + // Setup: ensure we have a known state + await appPage.evaluate(() => { + const np = window.NeptunesPride; + np.universe.interfaceSettings.screenPos = "left"; + // Center on a known star to have it in context + const star = np.universe.galaxy.stars[33]; // Hot Sham + if (star) { + np.npui.map.centerPointInMap(star.x, star.y); + } + // Make all stars visible so they appear in reports + for (const s in np.universe.galaxy.stars) { + np.universe.galaxy.stars[s].v = 1; + } + }); + + // Step 1: Open Compose Message & Autocomplete + await helper.step("messaging-composition-and-autocomplete", { + description: "NPA enhances the message composition screen with autocomplete for stars and players.", + verifications: [ + { + spec: "The compose screen is visible", + check: async () => { + await appPage.evaluate(() => { + const np = window.NeptunesPride; + const player = np.universe.galaxy.players[1]; + np.inbox.selectedMessage = { key: "compose" }; + np.inbox.commentDrafts["compose"] = ""; + np.crux.trigger("show_screen", "compose", player); + }); + await appPage.waitForSelector("textarea", { state: "visible" }); + await expect(appPage.locator("textarea")).toBeVisible(); + }, + }, + { + spec: "Typing [[ followed by part of a name and ] completes it", + check: async () => { + const textarea = appPage.locator("textarea"); + await textarea.fill("Hello, I am interested in "); + await textarea.focus(); + await appPage.keyboard.press("End"); + await appPage.keyboard.type("[[Hot S"); + await appPage.keyboard.press("]"); + + await expect(async () => { + const val = await appPage.locator("textarea").inputValue(); + expect(val).toContain("[[Hot Sham]]"); + }).toPass(); + }, + }, + ], + documentation: { + summary: "NPA's intelligent autocomplete works in any message composition area. By typing two square brackets followed by a partial name, you can quickly insert the full name of any star or player.", + howToUse: [ + "In the message box, type **[[** followed by a few letters of a star or player name (e.g., `[[Hot`).", + "Press **]** to automatically complete the name.", + ], + expectedResult: [ + "The partial text is replaced by the full name wrapped in brackets, such as `[[Hot Sham]]`.", + ], + }, + }); + + // Step 2: Messaging Helpers (Intel and Screenshot) + await helper.step("messaging-helpers", { + description: "The Intel and Screenshot buttons allow you to quickly share data and images.", + verifications: [ + { + spec: "The Intel and Screenshot buttons are visible in the reply box", + check: async () => { + // 1. Open a diplomacy detail screen (which uses NewMessageCommentBox) + await appPage.evaluate(() => { + const np = window.NeptunesPride; + const player = np.universe.galaxy.players[1]; + + // Mock a message so we can show diplomacy_detail + const msg = { + key: "msg1", + payload: { + from_uid: 1, + to_uids: [np.universe.player.uid], + subject: "Alliance?", + body: "Would you like to ally?" + }, + created: Date.now(), + comments: [], + commentsLoaded: true + }; + + np.inbox.messages.game_diplomacy = [msg]; + np.inbox.selectedMessage = msg; + np.inbox.cpage = 0; // Ensure we are on the first page to see the reply box + np.inbox.commentDrafts["msg1"] = ""; + np.crux.trigger("show_screen", "diplomacy_detail"); + }); + + // Wait for the reply box to appear + const textarea = appPage.locator("textarea"); + await expect(textarea).toBeVisible({ timeout: 10000 }); + const initialValue = await textarea.inputValue(); + + // 2. Trigger report via hotkey to populate clipboard + await appPage.keyboard.press("*"); + await appPage.waitForTimeout(500); + + // 3. Identify and click the Intel button + await appPage.evaluate(() => { + const all = [...document.querySelectorAll('*')].filter(e => + e.textContent?.trim() === 'Intel' && (e as HTMLElement).offsetParent !== null + ); + if (all.length >= 2) { + // The second one is likely the one in the message box (first is side menu) + (all[1] as HTMLElement).click(); + } else if (all.length === 1) { + (all[0] as HTMLElement).click(); + } else { + throw new Error("No visible Intel button found"); + } + }); + + // 4. Verify that content was added + await expect(async () => { + // Re-locate textarea as the screen might have re-rendered + const currentTextarea = appPage.locator("textarea"); + const newValue = await currentTextarea.inputValue(); + expect(newValue.length).toBeGreaterThan(initialValue.length); + expect(newValue).not.toContain("Error"); + }).toPass({ timeout: 10000 }); + }, + }, + ], + documentation: { + summary: "Sharing intelligence and visual data is essential for coordination. NPA provides dedicated buttons in the message composition area to automate these tasks.", + howToUse: [ + "View any report (e.g., by pressing **`**) to put it on your intelligence clipboard.", + "In a message box, click **Intel** to paste the last report.", + "Click **Screenshot** to capture your current map view and insert a shareable link.", + ], + expectedResult: [ + "Intelligence data or map screenshot links are automatically appended to your message draft.", + ], + }, + }); + + helper.generateArtifacts(); +}); diff --git a/tests/playwright/008-messaging-support/DOCS.md b/tests/playwright/008-messaging-support/DOCS.md new file mode 100644 index 00000000..982efd1b --- /dev/null +++ b/tests/playwright/008-messaging-support/DOCS.md @@ -0,0 +1,30 @@ +# Messaging Support + +NPA enhances the messaging experience with automated report insertion, screenshot sharing, and intelligent autocomplete for player and star names. + +## NPA enhances the message composition screen with autocomplete for stars and players. + +NPA's intelligent autocomplete works in any message composition area. By typing two square brackets followed by a partial name, you can quickly insert the full name of any star or player. + +![NPA enhances the message composition screen with autocomplete for stars and players.](./screenshots/000-messaging-composition-and-autocomplete.png) + +### How to use it +- In the message box, type **[[** followed by a few letters of a star or player name (e.g., `[[Hot`). +- Press **]** to automatically complete the name. + +### What to expect +- The partial text is replaced by the full name wrapped in brackets, such as `[[Hot Sham]]`. + +## The Intel and Screenshot buttons allow you to quickly share data and images. + +Sharing intelligence and visual data is essential for coordination. NPA provides dedicated buttons in the message composition area to automate these tasks. + +![The Intel and Screenshot buttons allow you to quickly share data and images.](./screenshots/001-messaging-helpers.png) + +### How to use it +- View any report (e.g., by pressing **`**) to put it on your intelligence clipboard. +- In a message box, click **Intel** to paste the last report. +- Click **Screenshot** to capture your current map view and insert a shareable link. + +### What to expect +- Intelligence data or map screenshot links are automatically appended to your message draft. diff --git a/tests/playwright/008-messaging-support/README.md b/tests/playwright/008-messaging-support/README.md new file mode 100644 index 00000000..8965c46c --- /dev/null +++ b/tests/playwright/008-messaging-support/README.md @@ -0,0 +1,22 @@ +# Messaging Support Validation + +Verify the message composition workflow, NPA messaging helpers (Intel/Screenshot buttons), and autocomplete functionality. + +Documentation target: `Messaging support` + +Companion user documentation: [DOCS.md](./DOCS.md) + +## NPA enhances the message composition screen with autocomplete for stars and players. + +![NPA enhances the message composition screen with autocomplete for stars and players.](./screenshots/000-messaging-composition-and-autocomplete.png) + +### Verifications +- [x] The compose screen is visible +- [x] Typing [[ followed by part of a name and ] completes it + +## The Intel and Screenshot buttons allow you to quickly share data and images. + +![The Intel and Screenshot buttons allow you to quickly share data and images.](./screenshots/001-messaging-helpers.png) + +### Verifications +- [x] The Intel and Screenshot buttons are visible in the reply box diff --git a/tests/playwright/008-messaging-support/screenshots/000-messaging-composition-and-autocomplete.png b/tests/playwright/008-messaging-support/screenshots/000-messaging-composition-and-autocomplete.png new file mode 100644 index 00000000..cf6827a9 Binary files /dev/null and b/tests/playwright/008-messaging-support/screenshots/000-messaging-composition-and-autocomplete.png differ diff --git a/tests/playwright/008-messaging-support/screenshots/001-messaging-helpers.png b/tests/playwright/008-messaging-support/screenshots/001-messaging-helpers.png new file mode 100644 index 00000000..60cfc642 Binary files /dev/null and b/tests/playwright/008-messaging-support/screenshots/001-messaging-helpers.png differ