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: 31 additions & 0 deletions apps/extension/src/lib/__tests__/step-buffer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,37 @@ describe("recording-step-buffer", () => {
});
});

it("records a reload of the current page instead of dropping it as a same-URL navigation", () => {
const buffer = {
steps: [],
navigation: { currentUrl: "https://example.com/a", pendingNavigation: false },
};
const result = observeRecordedNavigation(
buffer,
"https://example.com/a",
undefined,
"reload",
[],
);
expect(result).toEqual({ kind: "appended", index: 0 });
expect(buffer.steps[0]).toMatchObject({
op: "navigate",
url: "https://example.com/a",
transitionType: "reload",
});
});

it("still collapses the completion that follows a reload onto the recorded step", () => {
const buffer = {
steps: [],
navigation: { currentUrl: "https://example.com/a", pendingNavigation: false },
};
observeRecordedNavigation(buffer, "https://example.com/a", undefined, "reload", []);
// webNavigation.onCompleted reports the same URL and carries no transition type.
expect(observeRecordedNavigation(buffer, "https://example.com/a")).toEqual({ kind: "noop" });
expect(buffer.steps).toHaveLength(1);
});

it("asks the recorder to coalesce redirect hops instead of emitting each one", () => {
const buffer = {
steps: [],
Expand Down
7 changes: 6 additions & 1 deletion apps/extension/src/lib/recording/step-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@ export function observeRecordedNavigation(
transitionQualifiers?: string[],
): NavigationObserveResult {
const navigation = buffer.navigation;
if (!url || url === navigation.currentUrl) return { kind: "noop" };
// A reload commits to the URL the tab is already on, so the same-URL guard that
// collapses the onCommitted / onCompleted pair of one navigation would drop it
// too (issue #139). Only the committed event carries the transition type, so the
// completion that follows still collapses onto the recorded reload.
const isReload = transitionType === "reload";
if (!url || (url === navigation.currentUrl && !isReload)) return { kind: "noop" };
navigation.currentUrl = url;

const pendingIsCurrent =
Expand Down
33 changes: 33 additions & 0 deletions apps/extension/src/tools/__tests__/record-steps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,39 @@ describe("recorded user steps reach the exported trace", () => {
expect(step?.result.state).toBeTruthy();
});

it("records a reload of the page the recording is on", async () => {
// Issue #139: a reload commits to the URL the tab is already on, so it used to
// be dropped as the duplicate half of a navigation and the trace had no step
// for it, even though replaying the flow may depend on the refresh.
const chromeApi = installChrome();
const manager = fakeManager();
const tabsApi = makeTabsApi();
const sendToTab = vi.fn(async () => ({ ok: true }));

await handleRecordStart(manager, RECORD_START_V3, { tabsApi, sendToTab, cdp: makeFakeCdp() });

const details = { tabId: TAB_ID, frameId: 0, url: START_URL };
chromeApi.webNavigationOnCommitted.emit({
...details,
transitionType: "reload",
transitionQualifiers: [],
} as unknown as chrome.webNavigation.WebNavigationTransitionCallbackDetails);
chromeApi.webNavigationOnCompleted.emit(
details as unknown as chrome.webNavigation.WebNavigationFramedCallbackDetails,
);
// Let findRecordingForTab's tab lookup and the settle capture resolve.
await new Promise((resolve) => setTimeout(resolve, 0));

const stopped = await handleRecordStop(manager, { session_id: "abcd" }, { tabsApi, sendToTab });
const trace = (stopped as RecordStopResult).trace as TraceV3;

expect(trace.steps).toHaveLength(1);
const [step] = trace.steps;
expect(step).toMatchObject({ op: "navigate", to: START_URL, cause: "reload" });
expect(step?.state).toBeTruthy();
expect(step?.result.state).toBeTruthy();
});

it("reports an address-bar navigation from the page it started on, not the redirect hop", async () => {
const chromeApi = installChrome();
const manager = fakeManager();
Expand Down