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
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ await mkdir(outDir, { recursive: true });

const appTarget = await waitForAppTarget();
const app = await connectTarget(appTarget);
let externalTabId = null;
let deviceEmulationEnabled = false;
let findActive = false;
let browserUi = app;

try {
Expand Down Expand Up @@ -77,6 +80,7 @@ try {
assert(state.activeTabId !== null, "Active tab", "browser state has an active tab");
assert(activeTab(state)?.url === firstUrl, "Navigate", "active tab reached first smoke URL");
const tabId = state.activeTabId;
externalTabId = tabId;
await screenshot(browserUi, "smoke-browser-02-tab.png");

await wait(500);
Expand Down Expand Up @@ -137,6 +141,136 @@ try {
);
await screenshot(browserUi, "smoke-browser-04-navigation.png");

step("checking downloads internal page creation and reuse");
await callBridge("browserOpenInternalPage", { page: "downloads" });
state = await waitForBrowserState(
(s) => activeTab(s)?.internalPage === "downloads",
"downloads internal tab",
);
const downloadsTabId = state.activeTabId;
const downloadsTabCount = state.tabs.filter((tab) => tab.internalPage === "downloads").length;
const downloadsPage = await waitForInternalPage(downloadsTabId, "Download history");
assert(downloadsPage.headingVisible, "Downloads heading", "Download history heading is visible");
assert(
downloadsPage.activeTabWebview === false,
"Downloads guest omission",
"active downloads tab has no webview",
);
await screenshot(app, "smoke-browser-05-downloads.png");

step("checking passwords internal page creation and reuse");
await callBridge("browserOpenInternalPage", { page: "passwords" });
state = await waitForBrowserState(
(s) => activeTab(s)?.internalPage === "passwords",
"passwords internal tab",
);
const passwordsTabId = state.activeTabId;
const passwordsTabCount = state.tabs.filter((tab) => tab.internalPage === "passwords").length;
const passwordsPage = await waitForInternalPage(passwordsTabId, "Manage passwords");
assert(passwordsPage.headingVisible, "Passwords heading", "Manage passwords heading is visible");
assert(
passwordsPage.activeTabWebview === false,
"Passwords guest omission",
"active passwords tab has no webview",
);
await screenshot(app, "smoke-browser-06-passwords.png");

await callBridge("browserOpenInternalPage", { page: "downloads" });
state = await waitForBrowserState(
(s) => s.activeTabId === downloadsTabId,
"reused downloads internal tab",
);
assert(
state.tabs.filter((tab) => tab.internalPage === "downloads").length === downloadsTabCount,
"Reuse downloads tab",
"opening downloads again activates the existing internal tab",
);

await callBridge("browserOpenInternalPage", { page: "passwords" });
state = await waitForBrowserState(
(s) => s.activeTabId === passwordsTabId,
"reused passwords internal tab",
);
assert(
state.tabs.filter((tab) => tab.internalPage === "passwords").length === passwordsTabCount,
"Reuse passwords tab",
"opening passwords again activates the existing internal tab",
);

step("checking device emulation state and toolbar");
await callBridge("browserActivateTab", { tabId });
await waitForBrowserState((s) => s.activeTabId === tabId, "reactivated external smoke tab");
const emulation = {
width: 833,
height: 970,
deviceScaleFactor: 1,
scale: 1,
mobile: false,
touch: false,
preset: "Responsive",
};
await callBridge("browserSetDeviceEmulation", { tabId, emulation });
deviceEmulationEnabled = true;
state = await waitForBrowserState((s) => {
const active = activeTab(s);
return (
active?.tabId === tabId &&
active.deviceEmulation?.width === emulation.width &&
active.deviceEmulation?.height === emulation.height &&
active.deviceEmulation?.scale === emulation.scale &&
active.deviceEmulation?.preset === emulation.preset
);
}, "device emulation state");
assert(
activeTab(state)?.deviceEmulation?.preset === "Responsive",
"Device emulation state",
"active external tab exposes Responsive emulation state",
);
const deviceToolbar = await waitForDeviceToolbar(tabId, emulation.width, emulation.height);
assert(
deviceToolbar.visible,
"Device toolbar",
"device controls are visible with the expected dimensions",
);
assert(
deviceToolbar.activeTabWebview,
"Emulated guest preserved",
"active external tab keeps its webview while emulated",
);
await screenshot(app, "smoke-browser-07-device-toolbar.png");

await callBridge("browserSetDeviceEmulation", { tabId, emulation: null });
deviceEmulationEnabled = false;
state = await waitForBrowserState(
(s) => s.activeTabId === tabId && activeTab(s)?.deviceEmulation === undefined,
"cleared device emulation state",
);
assert(
activeTab(state)?.deviceEmulation === undefined,
"Clear device emulation",
"active external tab returns to its normal viewport",
);
const toolbarCleared = await evaluate(
`document.querySelector('[data-poracode-browser] [aria-label="Close device toolbar"]') === null`,
);
assert(toolbarCleared, "Hide device toolbar", "device controls disappear after clearing state");

step("checking Find in page against the generated data page");
await openFindBar();
findActive = true;
await setFindQuery("second page");
const findResult = await waitForFindResult("1 of 1");
assert(findResult.visible, "Find in page", "find bar reports one match in the data page");
await screenshot(app, "smoke-browser-08-find.png");
await clickButton("Close find");
findActive = false;
await waitFor(async () => {
const closed = await evaluate(
`document.querySelector('input[placeholder="Find in page"]') === null`,
);
return closed ? true : null;
}, "closed browser find bar");

step("checking Browser settings");
let settingsOpened = await settingsOverlayVisible();
if (!settingsOpened.ok) {
Expand All @@ -155,7 +289,7 @@ try {
});
if (settingsText.ok) {
assert(true, "Browser settings", "Browser settings page is reachable");
await screenshot(app, "smoke-browser-05-settings.png");
await screenshot(app, "smoke-browser-09-settings.png");
} else {
findings.push("Settings opened, but Browser settings page was not reachable by text click.");
}
Expand All @@ -179,6 +313,27 @@ try {
printReport(errors);
process.exitCode = results.some((result) => result.status === "FAIL") ? 1 : 0;
} finally {
if (findActive && externalTabId) {
try {
await clickButton("Close find", { optional: true });
await callBridge("browserStopFindInPage", {
tabId: externalTabId,
action: "clearSelection",
});
} catch (error) {
findings.push(`Find cleanup failed: ${error.message}`);
}
}
if (deviceEmulationEnabled && externalTabId) {
try {
await callBridge("browserSetDeviceEmulation", {
tabId: externalTabId,
emulation: null,
});
} catch (error) {
findings.push(`Device emulation cleanup failed: ${error.message}`);
}
}
if (browserUi !== app) browserUi.close();
app.close();
}
Expand Down Expand Up @@ -466,6 +621,106 @@ function activeTab(state) {
return state.tabs.find((tab) => tab.tabId === state.activeTabId);
}

async function waitForInternalPage(tabId, heading) {
return waitFor(async () => {
const snapshot = await evaluate(
`(() => {
const root = document.querySelector('[data-poracode-browser]');
const tabId = ${JSON.stringify(tabId)};
const headingText = ${JSON.stringify(heading)};
const heading = [...(root?.querySelectorAll('h1') ?? [])].find(
(candidate) => candidate.textContent?.trim() === headingText,
);
return {
headingVisible: Boolean(heading && heading.getClientRects().length > 0),
activeTabWebview: Boolean(root?.querySelector('webview[data-tab-id="' + CSS.escape(tabId) + '"]')),
};
})()`,
);
return snapshot.headingVisible ? snapshot : null;
}, `${heading} internal page`);
}

async function waitForDeviceToolbar(tabId, width, height) {
return waitFor(async () => {
const snapshot = await evaluate(
`(() => {
const root = document.querySelector('[data-poracode-browser]');
const widthInput = root?.querySelector('[aria-label="Viewport width"]');
const heightInput = root?.querySelector('[aria-label="Viewport height"]');
const close = root?.querySelector('[aria-label="Close device toolbar"]');
const tabId = ${JSON.stringify(tabId)};
return {
visible: Boolean(close && close.getClientRects().length > 0) &&
widthInput?.value === ${JSON.stringify(String(width))} &&
heightInput?.value === ${JSON.stringify(String(height))},
activeTabWebview: Boolean(root?.querySelector('webview[data-tab-id="' + CSS.escape(tabId) + '"]')),
};
})()`,
);
return snapshot.visible ? snapshot : null;
}, "device toolbar dimensions");
}

async function openFindBar() {
await clickButton("Browser menu");
await waitFor(async () => {
const clicked = await evaluate(
`(() => {
const item = [...document.querySelectorAll('[role="menuitem"]')].find((candidate) =>
candidate.querySelector('label')?.textContent?.trim() === "Find in page"
);
if (!item || item.getAttribute('aria-disabled') === 'true') return false;
item.click();
return true;
})()`,
);
return clicked ? true : null;
}, "Find in page menu item");
await waitFor(async () => {
const visible = await evaluate(
`Boolean(document.querySelector('input[placeholder="Find in page"]'))`,
);
return visible ? true : null;
}, "browser find bar");
}

async function setFindQuery(query) {
const focused = await evaluate(
`(() => {
const input = document.querySelector('input[placeholder="Find in page"]');
if (!input) return false;
input.focus();
input.select();
return true;
})()`,
);
if (!focused) throw new Error("Browser find input was not available");
await send(app, "Input.insertText", { text: query });
await waitFor(async () => {
const value = await evaluate(
`document.querySelector('input[placeholder="Find in page"]')?.value ?? null`,
);
return value === query ? true : null;
}, "browser find query input");
}

async function waitForFindResult(counter) {
return waitFor(async () => {
const snapshot = await evaluate(
`(() => {
const input = document.querySelector('input[placeholder="Find in page"]');
const search = input?.closest('[role="search"]');
return {
visible: Boolean(search && search.getClientRects().length > 0),
text: search?.textContent ?? '',
};
})()`,
);
return snapshot.visible && snapshot.text.includes(counter) ? snapshot : null;
}, `browser find result ${counter}`);
}

async function callBridge(method, payload) {
const result = await evaluate(
`(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ export const functionalAreas = [
},
{
id: "browser",
title: "In-app Browser, browser MCP, picker, bookmarks, and Chrome extension",
title:
"In-app Browser, internal pages, downloads, passwords, device emulation, find, browser MCP, picker, bookmarks, and Chrome extension",
patterns: [/browser/i, /chrome-extension/i, /ChromeMcp/i, /picker/i],
automated: ["baseline", "browser"],
manual: [],
Expand Down
Loading