Fix web push delivery handling and worker console
Some checks failed
docker-images / resolve-build-targets (push) Successful in 5s
docker-images / build-and-push (admin) (push) Successful in 30s
docker-images / submit-indexnow (push) Has been cancelled
docker-images / build-and-push (frontend) (push) Has been cancelled
docker-images / build-and-push (backend) (push) Has been cancelled
Some checks failed
docker-images / resolve-build-targets (push) Successful in 5s
docker-images / build-and-push (admin) (push) Successful in 30s
docker-images / submit-indexnow (push) Has been cancelled
docker-images / build-and-push (frontend) (push) Has been cancelled
docker-images / build-and-push (backend) (push) Has been cancelled
This commit is contained in:
68
playwright-smoke/playwright.web-push.config.ts
Normal file
68
playwright-smoke/playwright.web-push.config.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
const backendBaseUrl = "http://127.0.0.1:5150";
|
||||
const frontendBaseUrl = "http://127.0.0.1:4321";
|
||||
const isCi = Boolean(process.env.CI);
|
||||
const webPushChannel =
|
||||
process.env.PLAYWRIGHT_WEB_PUSH_CHANNEL ??
|
||||
(process.platform === "win32" ? "msedge" : undefined);
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
testMatch: /web-push\.real\.spec\.ts/,
|
||||
fullyParallel: false,
|
||||
workers: 1,
|
||||
timeout: 180_000,
|
||||
expect: {
|
||||
timeout: 20_000,
|
||||
},
|
||||
reporter: [["list"]],
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
channel: webPushChannel,
|
||||
baseURL: frontendBaseUrl,
|
||||
headless: true,
|
||||
trace: "on-first-retry",
|
||||
screenshot: "only-on-failure",
|
||||
video: "retain-on-failure",
|
||||
},
|
||||
webServer: [
|
||||
{
|
||||
command: "cargo loco start --server-and-worker",
|
||||
cwd: path.resolve(repoRoot, "backend"),
|
||||
url: `${backendBaseUrl}/api/site_settings`,
|
||||
reuseExistingServer: false,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...process.env,
|
||||
DATABASE_URL:
|
||||
process.env.DATABASE_URL ??
|
||||
"postgres://postgres:postgres%402025%21@10.0.0.2:5432/termi-api_development",
|
||||
REDIS_URL: process.env.REDIS_URL ?? "redis://127.0.0.1:6379",
|
||||
TERMI_ADMIN_LOCAL_LOGIN_ENABLED:
|
||||
process.env.TERMI_ADMIN_LOCAL_LOGIN_ENABLED ?? "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
command: "pnpm dev --host 127.0.0.1 --port 4321",
|
||||
cwd: path.resolve(repoRoot, "frontend"),
|
||||
url: frontendBaseUrl,
|
||||
reuseExistingServer: false,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...process.env,
|
||||
PUBLIC_API_BASE_URL: `${backendBaseUrl}/api`,
|
||||
INTERNAL_API_BASE_URL: `${backendBaseUrl}/api`,
|
||||
PUBLIC_IMAGE_ALLOWED_HOSTS: "127.0.0.1,127.0.0.1:5150",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
122
playwright-smoke/tests/web-push.real.spec.ts
Normal file
122
playwright-smoke/tests/web-push.real.spec.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { chromium, expect, test } from "@playwright/test";
|
||||
|
||||
const BACKEND_BASE_URL = "http://127.0.0.1:5150";
|
||||
const FRONTEND_BASE_URL = "http://127.0.0.1:4321";
|
||||
|
||||
type AdminSubscriptionRecord = {
|
||||
id: number;
|
||||
channel_type: string;
|
||||
target: string;
|
||||
};
|
||||
|
||||
type AdminSubscriptionListResponse = {
|
||||
subscriptions: AdminSubscriptionRecord[];
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__termiPushMessages?: unknown[];
|
||||
__termiSubscriptionPopupReady?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
test("浏览器订阅后可以收到测试推送", async ({ request }, testInfo) => {
|
||||
const context = await chromium.launchPersistentContext(
|
||||
testInfo.outputPath("web-push-user-data"),
|
||||
{
|
||||
headless: true,
|
||||
viewport: { width: 1280, height: 800 },
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
await context.grantPermissions(["notifications"], {
|
||||
origin: FRONTEND_BASE_URL,
|
||||
});
|
||||
|
||||
const page = context.pages()[0] ?? (await context.newPage());
|
||||
|
||||
await page.addInitScript(() => {
|
||||
window.__termiPushMessages = [];
|
||||
navigator.serviceWorker?.addEventListener("message", (event) => {
|
||||
if (event.data?.type === "termi:web-push-received") {
|
||||
window.__termiPushMessages?.push(event.data.payload ?? null);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto(`${FRONTEND_BASE_URL}/maintenance?returnTo=%2F`);
|
||||
await page.getByLabel("访问口令").fill("termi");
|
||||
await page.getByRole("button", { name: "进入站点" }).click();
|
||||
await page.waitForURL(`${FRONTEND_BASE_URL}/`);
|
||||
|
||||
await page.waitForFunction(
|
||||
() => window.__termiSubscriptionPopupReady === true,
|
||||
);
|
||||
|
||||
await page.locator("[data-subscription-popup-open]").click();
|
||||
|
||||
const subscribeResponsePromise = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes("/api/subscriptions/combined") &&
|
||||
response.request().method() === "POST",
|
||||
);
|
||||
|
||||
await page.locator("[data-subscription-popup-submit]").click();
|
||||
|
||||
const subscribeResponse = await subscribeResponsePromise;
|
||||
expect(subscribeResponse.ok()).toBeTruthy();
|
||||
await expect(
|
||||
page.locator('[data-subscription-popup-status][data-state="success"]'),
|
||||
).toBeVisible();
|
||||
|
||||
const endpoint = await page.evaluate(async () => {
|
||||
const registration = await navigator.serviceWorker.ready;
|
||||
const subscription = await registration.pushManager.getSubscription();
|
||||
return subscription?.endpoint ?? null;
|
||||
});
|
||||
|
||||
expect(endpoint).toBeTruthy();
|
||||
|
||||
const loginResponse = await request.post(
|
||||
`${BACKEND_BASE_URL}/api/admin/session/login`,
|
||||
{
|
||||
data: {
|
||||
username: "admin",
|
||||
password: "admin123",
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(loginResponse.ok()).toBeTruthy();
|
||||
|
||||
const subscriptionsResponse = await request.get(
|
||||
`${BACKEND_BASE_URL}/api/admin/subscriptions`,
|
||||
);
|
||||
expect(subscriptionsResponse.ok()).toBeTruthy();
|
||||
const subscriptionsPayload =
|
||||
(await subscriptionsResponse.json()) as AdminSubscriptionListResponse;
|
||||
const subscription = subscriptionsPayload.subscriptions.find(
|
||||
(item) => item.channel_type === "web_push" && item.target === endpoint,
|
||||
);
|
||||
|
||||
expect(subscription).toBeTruthy();
|
||||
|
||||
const testResponse = await request.post(
|
||||
`${BACKEND_BASE_URL}/api/admin/subscriptions/${subscription?.id}/test`,
|
||||
);
|
||||
expect(testResponse.ok()).toBeTruthy();
|
||||
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
Array.isArray(window.__termiPushMessages) &&
|
||||
window.__termiPushMessages.length > 0,
|
||||
undefined,
|
||||
{ timeout: 45_000 },
|
||||
);
|
||||
|
||||
const messages = await page.evaluate(() => window.__termiPushMessages ?? []);
|
||||
expect(messages.length).toBeGreaterThan(0);
|
||||
} finally {
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user