Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions bin/version.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,14 +14,29 @@ export type VersionInfo = {
export async function getVersionInfo(): Promise<VersionInfo> {
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 ?? "",
};
Expand Down
48 changes: 24 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions scripts/setup-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/intel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
165 changes: 165 additions & 0 deletions tests/playwright/008-messaging-support/008-messaging-support.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
});
Loading