fix navigation and related tests

This commit is contained in:
Muki Kiboigo
2025-10-10 04:02:42 -07:00
parent f97697535f
commit e9b08f19cf
14 changed files with 168 additions and 146 deletions

View File

@@ -81,7 +81,9 @@ fn _dispatchPopStateEvent(state: ?[]const u8, page: *Page) !void {
pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { pub fn _pushState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
const arena = page.session.arena; const arena = page.session.arena;
const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw); const url = if (_url) |u| try arena.dupe(u8, u) else try arena.dupe(u8, page.url.raw);
_ = try page.session.navigation.pushEntry(url, .{ .state = state }, page);
const json = state.toJson(arena) catch return error.DataClone;
_ = try page.session.navigation.pushEntry(url, json, page);
} }
pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void { pub fn _replaceState(_: *const History, state: js.Object, _: ?[]const u8, _url: ?[]const u8, page: *Page) !void {
@@ -113,7 +115,7 @@ pub fn go(_: *const History, delta: i32, page: *Page) !void {
} }
} }
_ = try entry.navigate(page, .force); _ = try page.session.navigation.navigate(entry.url, .{ .traverse = index }, page);
} }
pub fn _go(self: *History, _delta: ?i32, page: *Page) !void { pub fn _go(self: *History, _delta: ?i32, page: *Page) !void {

View File

@@ -34,16 +34,24 @@ const Navigation = @This();
pub const Interfaces = .{ pub const Interfaces = .{
Navigation, Navigation,
NavigationActivation, NavigationActivation,
NavigationTransition,
NavigationHistoryEntry, NavigationHistoryEntry,
}; };
pub const NavigationKind = union(enum) {
initial,
push: ?[]const u8,
replace,
traverse: usize,
reload,
};
pub const prototype = *EventTarget; pub const prototype = *EventTarget;
base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .plain }, base: parser.EventTargetTBase = parser.EventTargetTBase{ .internal_target_type = .plain },
index: usize = 0, index: usize = 0,
entries: std.ArrayListUnmanaged(NavigationHistoryEntry) = .empty, entries: std.ArrayListUnmanaged(NavigationHistoryEntry) = .empty,
next_entry_id: usize = 0, next_entry_id: usize = 0,
// TODO: key->index mapping
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry // https://developer.mozilla.org/en-US/docs/Web/API/NavigationHistoryEntry
const NavigationHistoryEntry = struct { const NavigationHistoryEntry = struct {
@@ -91,49 +99,17 @@ const NavigationHistoryEntry = struct {
return null; return null;
} }
} }
pub fn navigate(entry: NavigationHistoryEntry, reload: enum { none, force }, page: *Page) !NavigationReturn {
const arena = page.session.arena;
const url = entry.url orelse return error.MissingURL;
// https://github.com/WICG/navigation-api/issues/95
//
// These will only settle on same-origin navigation (mostly intended for SPAs).
// It is fine (and expected) for these to not settle on cross-origin requests :)
const committed = try page.js.createPromiseResolver(.page);
const finished = try page.js.createPromiseResolver(.page);
const new_url = try URL.parse(url, null);
if (try page.url.eqlDocument(&new_url, arena) or reload == .force) {
page.url = new_url;
try committed.resolve({});
// todo: Fire navigate event
try finished.resolve({});
} else {
// TODO: Change to history
try page.navigateFromWebAPI(url, .{ .reason = .history });
}
return .{
.committed = committed.promise(),
.finished = finished.promise(),
};
}
}; };
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation // https://developer.mozilla.org/en-US/docs/Web/API/NavigationActivation
const NavigationActivation = struct { const NavigationActivation = struct {
const NavigationActivationType = enum { const NavigationActivationType = enum {
pub const ENUM_JS_USE_TAG = true;
push, push,
reload, reload,
replace, replace,
traverse, traverse,
pub fn toString(self: NavigationActivationType) []const u8 {
return @tagName(self);
}
}; };
entry: NavigationHistoryEntry, entry: NavigationHistoryEntry,
@@ -153,6 +129,13 @@ const NavigationActivation = struct {
} }
}; };
// https://developer.mozilla.org/en-US/docs/Web/API/NavigationTransition
const NavigationTransition = struct {
finished: js.Promise,
from: NavigationHistoryEntry,
navigation_type: NavigationActivation.NavigationActivationType,
};
pub fn get_canGoBack(self: *const Navigation) bool { pub fn get_canGoBack(self: *const Navigation) bool {
return self.index > 0; return self.index > 0;
} }
@@ -169,6 +152,11 @@ pub fn get_currentEntry(self: *const Navigation) NavigationHistoryEntry {
return self.entries.items[self.index]; return self.entries.items[self.index];
} }
pub fn get_transition(_: *const Navigation) ?NavigationTransition {
// For now, all transitions are just considered complete.
return null;
}
const NavigationReturn = struct { const NavigationReturn = struct {
committed: js.Promise, committed: js.Promise,
finished: js.Promise, finished: js.Promise,
@@ -183,7 +171,7 @@ pub fn _back(self: *Navigation, page: *Page) !NavigationReturn {
const next_entry = self.entries.items[new_index]; const next_entry = self.entries.items[new_index];
self.index = new_index; self.index = new_index;
return next_entry.navigate(.none, page); return self.navigate(next_entry.url, .{ .traverse = new_index }, page);
} }
pub fn _entries(self: *const Navigation) []NavigationHistoryEntry { pub fn _entries(self: *const Navigation) []NavigationHistoryEntry {
@@ -199,15 +187,33 @@ pub fn _forward(self: *Navigation, page: *Page) !NavigationReturn {
const next_entry = self.entries.items[new_index]; const next_entry = self.entries.items[new_index];
self.index = new_index; self.index = new_index;
return next_entry.navigate(.none, page); return self.navigate(next_entry.url, .{ .traverse = new_index }, page);
}
// This is for after true navigation processing, where we need to ensure that our entries are up to date.
pub fn processNavigation(self: *Navigation, url: []const u8, kind: NavigationKind, page: *Page) !void {
switch (kind) {
.initial => {
_ = try self.pushEntry(url, null, page);
},
.replace => {
// When replacing, we just update the URL but the state is nullified.
const entry = self.currentEntry();
entry.url = url;
entry.state = null;
},
.push => |state| {
_ = try self.pushEntry(url, state, page);
},
.traverse, .reload => {},
}
} }
/// Pushes an entry into the Navigation stack WITHOUT actually navigating to it. /// Pushes an entry into the Navigation stack WITHOUT actually navigating to it.
/// For that, use `navigate`. /// For that, use `navigate`.
pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions, page: *Page) !NavigationHistoryEntry { pub fn pushEntry(self: *Navigation, _url: ?[]const u8, state: ?[]const u8, page: *Page) !NavigationHistoryEntry {
const arena = page.session.arena; const arena = page.session.arena;
const options = _opts orelse NavigateOptions{};
const url = if (_url) |u| try arena.dupe(u8, u) else null; const url = if (_url) |u| try arena.dupe(u8, u) else null;
// truncates our history here. // truncates our history here.
@@ -221,14 +227,6 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions,
const id_str = try std.fmt.allocPrint(arena, "{d}", .{id}); const id_str = try std.fmt.allocPrint(arena, "{d}", .{id});
const state: ?[]const u8 = blk: {
if (options.state) |s| {
break :blk s.toJson(arena) catch return error.DataClone;
} else {
break :blk null;
}
};
const entry = NavigationHistoryEntry{ const entry = NavigationHistoryEntry{
.id = id_str, .id = id_str,
.key = id_str, .key = id_str,
@@ -237,7 +235,6 @@ pub fn pushEntry(self: *Navigation, _url: ?[]const u8, _opts: ?NavigateOptions,
}; };
try self.entries.append(arena, entry); try self.entries.append(arena, entry);
return entry; return entry;
} }
@@ -255,9 +252,67 @@ const NavigateOptions = struct {
history: NavigateOptionsHistory = .auto, history: NavigateOptionsHistory = .auto,
}; };
pub fn navigate(
self: *Navigation,
_url: ?[]const u8,
kind: NavigationKind,
page: *Page,
) !NavigationReturn {
const arena = page.session.arena;
const url = _url orelse return error.MissingURL;
// https://github.com/WICG/navigation-api/issues/95
//
// These will only settle on same-origin navigation (mostly intended for SPAs).
// It is fine (and expected) for these to not settle on cross-origin requests :)
const committed = try page.js.createPromiseResolver(.page);
const finished = try page.js.createPromiseResolver(.page);
const new_url = try URL.parse(url, null);
const is_same_document = try page.url.eqlDocument(&new_url, arena);
switch (kind) {
.push => |state| {
if (is_same_document) {
page.url = new_url;
try committed.resolve({});
// todo: Fire navigate event
try finished.resolve({});
_ = try self.pushEntry(url, state, page);
} else {
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
}
},
.traverse => |index| {
self.index = index;
if (is_same_document) {
page.url = new_url;
try committed.resolve({});
// todo: Fire navigate event
try finished.resolve({});
} else {
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
}
},
.reload => {
try page.navigateFromWebAPI(url, .{ .reason = .navigation }, kind);
},
else => unreachable,
}
return .{
.committed = committed.promise(),
.finished = finished.promise(),
};
}
pub fn _navigate(self: *Navigation, _url: []const u8, _opts: ?NavigateOptions, page: *Page) !NavigationReturn { pub fn _navigate(self: *Navigation, _url: []const u8, _opts: ?NavigateOptions, page: *Page) !NavigationReturn {
const entry = try self.pushEntry(_url, _opts, page); const opts = _opts orelse NavigateOptions{};
return entry.navigate(.none, page); const json = if (opts.state) |state| state.toJson(page.session.arena) catch return error.DataClone else null;
return try self.navigate(_url, .{ .push = json }, page);
} }
pub const ReloadOptions = struct { pub const ReloadOptions = struct {
@@ -274,15 +329,23 @@ pub fn _reload(self: *Navigation, _opts: ?ReloadOptions, page: *Page) !Navigatio
entry.state = state.toJson(arena) catch return error.DataClone; entry.state = state.toJson(arena) catch return error.DataClone;
} }
return entry.navigate(.force, page); return self.navigate(entry.url, .reload, page);
} }
pub fn _transition(_: *const Navigation) !NavigationReturn { pub const TraverseToOptions = struct {
unreachable; info: ?js.Object = null,
} };
pub fn _traverseTo(_: *const Navigation, _: []const u8) !NavigationReturn { pub fn _traverseTo(self: *Navigation, key: []const u8, _: ?TraverseToOptions, page: *Page) !NavigationReturn {
unreachable; // const opts = _opts orelse TraverseToOptions{};
for (self.entries.items, 0..) |entry, i| {
if (std.mem.eql(u8, key, entry.key)) {
return try self.navigate(entry.url, .{ .traverse = i }, page);
}
}
return error.InvalidStateError;
} }
pub const UpdateCurrentEntryOptions = struct { pub const UpdateCurrentEntryOptions = struct {

View File

@@ -195,7 +195,7 @@ pub const HTMLDocument = struct {
} }
pub fn set_location(_: *const parser.DocumentHTML, url: []const u8, page: *Page) !void { pub fn set_location(_: *const parser.DocumentHTML, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script }); return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
} }
pub fn get_designMode(_: *parser.DocumentHTML) []const u8 { pub fn get_designMode(_: *parser.DocumentHTML) []const u8 {

View File

@@ -74,15 +74,15 @@ pub const Location = struct {
} }
pub fn _assign(_: *const Location, url: []const u8, page: *Page) !void { pub fn _assign(_: *const Location, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script }); return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
} }
pub fn _replace(_: *const Location, url: []const u8, page: *Page) !void { pub fn _replace(_: *const Location, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script }); return page.navigateFromWebAPI(url, .{ .reason = .script }, .replace);
} }
pub fn _reload(_: *const Location, page: *Page) !void { pub fn _reload(_: *const Location, page: *Page) !void {
return page.navigateFromWebAPI(page.url.raw, .{ .reason = .script }); return page.navigateFromWebAPI(page.url.raw, .{ .reason = .script }, .reload);
} }
pub fn _toString(self: *Location, page: *Page) ![]const u8 { pub fn _toString(self: *Location, page: *Page) ![]const u8 {

View File

@@ -143,7 +143,7 @@ pub const Window = struct {
} }
pub fn set_location(_: *const Window, url: []const u8, page: *Page) !void { pub fn set_location(_: *const Window, url: []const u8, page: *Page) !void {
return page.navigateFromWebAPI(url, .{ .reason = .script }); return page.navigateFromWebAPI(url, .{ .reason = .script }, .{ .push = null });
} }
// frames return the window itself, but accessing it via a pseudo // frames return the window itself, but accessing it via a pseudo

View File

@@ -34,6 +34,7 @@ const Http = @import("../http/Http.zig");
const ScriptManager = @import("ScriptManager.zig"); const ScriptManager = @import("ScriptManager.zig");
const SlotChangeMonitor = @import("SlotChangeMonitor.zig"); const SlotChangeMonitor = @import("SlotChangeMonitor.zig");
const HTMLDocument = @import("html/document.zig").HTMLDocument; const HTMLDocument = @import("html/document.zig").HTMLDocument;
const NavigationKind = @import("html/Navigation.zig").NavigationKind;
const js = @import("js/js.zig"); const js = @import("js/js.zig");
const URL = @import("../url.zig").URL; const URL = @import("../url.zig").URL;
@@ -815,8 +816,8 @@ pub const Page = struct {
}, },
} }
// Push the navigation after a successful load. // We need to handle different navigation types differently.
_ = try self.session.navigation.pushEntry(self.url.raw, null, self); try self.session.navigation.processNavigation(self.url.raw, self.session.navigation_kind, self);
} }
fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void { fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void {
@@ -906,7 +907,7 @@ pub const Page = struct {
.a => { .a => {
const element: *parser.Element = @ptrCast(node); const element: *parser.Element = @ptrCast(node);
const href = (try parser.elementGetAttribute(element, "href")) orelse return; const href = (try parser.elementGetAttribute(element, "href")) orelse return;
try self.navigateFromWebAPI(href, .{}); try self.navigateFromWebAPI(href, .{}, .{ .push = null });
}, },
.input => { .input => {
const element: *parser.Element = @ptrCast(node); const element: *parser.Element = @ptrCast(node);
@@ -1043,7 +1044,7 @@ pub const Page = struct {
// As such we schedule the function to be called as soon as possible. // As such we schedule the function to be called as soon as possible.
// The page.arena is safe to use here, but the transfer_arena exists // The page.arena is safe to use here, but the transfer_arena exists
// specifically for this type of lifetime. // specifically for this type of lifetime.
pub fn navigateFromWebAPI(self: *Page, url: []const u8, opts: NavigateOpts) !void { pub fn navigateFromWebAPI(self: *Page, url: []const u8, opts: NavigateOpts, kind: NavigationKind) !void {
const session = self.session; const session = self.session;
if (session.queued_navigation != null) { if (session.queued_navigation != null) {
// It might seem like this should never happen. And it might not, // It might seem like this should never happen. And it might not,
@@ -1070,6 +1071,8 @@ pub const Page = struct {
.url = try URL.stitch(session.transfer_arena, url, self.url.raw, .{ .alloc = .always }), .url = try URL.stitch(session.transfer_arena, url, self.url.raw, .{ .alloc = .always }),
}; };
session.navigation_kind = kind;
self.http_client.abort(); self.http_client.abort();
// In v8, this throws an exception which JS code cannot catch. // In v8, this throws an exception which JS code cannot catch.
@@ -1120,7 +1123,7 @@ pub const Page = struct {
} else { } else {
action = try URL.concatQueryString(transfer_arena, action, buf.items); action = try URL.concatQueryString(transfer_arena, action, buf.items);
} }
try self.navigateFromWebAPI(action, opts); try self.navigateFromWebAPI(action, opts, .{ .push = null });
} }
pub fn isNodeAttached(self: *const Page, node: *parser.Node) bool { pub fn isNodeAttached(self: *const Page, node: *parser.Node) bool {
@@ -1178,6 +1181,7 @@ pub const NavigateReason = enum {
form, form,
script, script,
history, history,
navigation,
}; };
pub const NavigateOpts = struct { pub const NavigateOpts = struct {

View File

@@ -22,6 +22,7 @@ const Allocator = std.mem.Allocator;
const js = @import("js/js.zig"); const js = @import("js/js.zig");
const Page = @import("page.zig").Page; const Page = @import("page.zig").Page;
const NavigationKind = @import("html/Navigation.zig").NavigationKind;
const Browser = @import("browser.zig").Browser; const Browser = @import("browser.zig").Browser;
const NavigateOpts = @import("page.zig").NavigateOpts; const NavigateOpts = @import("page.zig").NavigateOpts;
const History = @import("html/History.zig"); const History = @import("html/History.zig");
@@ -59,6 +60,7 @@ pub const Session = struct {
// https://developer.mozilla.org/en-US/docs/Web/API/History // https://developer.mozilla.org/en-US/docs/Web/API/History
history: History = .{}, history: History = .{},
navigation: Navigation = .{}, navigation: Navigation = .{},
navigation_kind: NavigationKind = .initial,
page: ?Page = null, page: ?Page = null,

View File

@@ -174,7 +174,7 @@ pub fn pageNavigate(arena: Allocator, bc: anytype, event: *const Notification.Pa
var cdp = bc.cdp; var cdp = bc.cdp;
const reason_: ?[]const u8 = switch (event.opts.reason) { const reason_: ?[]const u8 = switch (event.opts.reason) {
.anchor => "anchorClick", .anchor => "anchorClick",
.script, .history => "scriptInitiated", .script, .history, .navigation => "scriptInitiated",
.form => switch (event.opts.method) { .form => switch (event.opts.method) {
.GET => "formSubmissionGet", .GET => "formSubmissionGet",
.POST => "formSubmissionPost", .POST => "formSubmissionPost",

View File

@@ -402,19 +402,13 @@ pub fn htmlRunner(file: []const u8) !void {
const url = try std.fmt.allocPrint(arena_allocator, "http://localhost:9582/src/tests/{s}", .{file}); const url = try std.fmt.allocPrint(arena_allocator, "http://localhost:9582/src/tests/{s}", .{file});
try page.navigate(url, .{}); try page.navigate(url, .{});
_ = page.wait(2000); test_session.fetchWait(2000);
// page exits more aggressively in tests. We want to make sure this is called // page exits more aggressively in tests. We want to make sure this is called
// at lease once. // at lease once.
page.session.browser.runMicrotasks(); page.session.browser.runMicrotasks();
page.session.browser.runMessageLoop(); page.session.browser.runMessageLoop();
const needs_second_wait = try js_context.exec("testing._onPageWait.length > 0", "check_onPageWait");
if (needs_second_wait.value.toBool(page.js.isolate)) {
// sets the isSecondWait flag in testing.
_ = js_context.exec("testing._isSecondWait = true", "set_second_wait_flag") catch {};
_ = page.wait(2000);
}
@import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start); @import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start);
const value = js_context.exec("testing.getStatus()", "testing.getStatus()") catch |err| { const value = js_context.exec("testing.getStatus()", "testing.getStatus()") catch |err| {

View File

@@ -11,10 +11,12 @@
testing.expectEqual('auto', history.scrollRestoration); testing.expectEqual('auto', history.scrollRestoration);
testing.expectEqual(null, history.state) testing.expectEqual(null, history.state)
history.pushState({ testInProgress: true }, null, 'http://127.0.0.1:9582/xhr/json'); history.pushState({ testInProgress: true }, null, 'http://127.0.0.1:9582/src/tests/html/history2.html');
testing.expectEqual({ testInProgress: true }, history.state); testing.expectEqual({ testInProgress: true }, history.state);
history.pushState({ testInProgress: false }, null, 'http://127.0.0.1:9582/xhr/json');
history.replaceState({ "new": "field", testComplete: true }, null); history.replaceState({ "new": "field", testComplete: true }, null);
let state = { "new": "field", testComplete: true }; let state = { "new": "field", testComplete: true };
testing.expectEqual(state, history.state); testing.expectEqual(state, history.state);
@@ -31,10 +33,5 @@
testing.expectEqual(state, popstateEventState); testing.expectEqual(state, popstateEventState);
}) })
testing.onPageWait(() => { history.back();
testing.expectEqual(true, history.state && history.state.testComplete);
testing.expectEqual(state, history.state);
});
testing.expectEqual(undefined, history.go());
</script> </script>

View File

@@ -0,0 +1,6 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id=history2>
testing.expectEqual(true, history.state && history.state.testInProgress);
</script>

View File

@@ -1,5 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<script src="../testing.js"></script> <script src="../testing.js"></script>
<script id=navigation> <script id=navigation>
testing.expectEqual('object', typeof navigation); testing.expectEqual('object', typeof navigation);
testing.expectEqual('object', typeof navigation.currentEntry); testing.expectEqual('object', typeof navigation.currentEntry);
@@ -7,43 +8,11 @@
testing.expectEqual('string', typeof navigation.currentEntry.id); testing.expectEqual('string', typeof navigation.currentEntry.id);
testing.expectEqual('string', typeof navigation.currentEntry.key); testing.expectEqual('string', typeof navigation.currentEntry.key);
testing.expectEqual('string', typeof navigation.currentEntry.url); testing.expectEqual('string', typeof navigation.currentEntry.url);
testing.expectEqual(0, navigation.currentEntry.index);
testing.expectEqual(true, navigation.currentEntry.sameDocument); const currentIndex = navigation.currentEntry.index;
let result = navigation.navigate('http://127.0.0.1:9582/xhr/json', { navigation.navigate(
state: { testInProgress: true } 'http://127.0.0.1:9582/src/tests/html/navigation2.html',
}); { state: { currentIndex: currentIndex, navTestInProgress: true } }
testing.expectEqual('object', typeof result); );
testing.expectEqual('object', typeof result.committed);
testing.expectEqual('object', typeof result.finished);
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
testing.expectEqual(1, navigation.currentEntry.index);
testing.expectEqual(true, navigation.canGoBack);
testing.expectEqual(false, navigation.canGoForward);
testing.expectEqual(undefined, navigation.back());
testing.onPageWait(() => {
testing.expectEqual(0, navigation.currentEntry.index);
testing.expectEqual(true, navigation.canGoForward);
testing.expectEqual(undefined, navigation.forward());
});
testing.onPageWait(() => {
testing.expectEqual(1, navigation.currentEntry.index);
testing.expectEqual({ testInProgress: true }, navigation.currentEntry.getState());
let targetKey = navigation.currentEntry.key;
testing.expectEqual(undefined, navigation.traverseTo(targetKey));
});
navigation.updateCurrentEntry({ state: { updated: true, testComplete: true } });
testing.expectEqual({ updated: true, testComplete: true }, navigation.currentEntry.getState());
testing.onPageWait(() => {
testing.expectEqual(true, navigation.currentEntry.getState().testComplete);
});
</script> </script>

View File

@@ -0,0 +1,8 @@
<!DOCTYPE html>
<script src="../testing.js"></script>
<script id=navigation2>
const state = navigation.currentEntry.getState();
testing.expectEqual(true, state.navTestInProgress);
testing.expectEqual(state.currentIndex + 1, navigation.currentEntry.index);
</script>

View File

@@ -51,14 +51,6 @@
// if we're already in a fail state, return fail, nothing can recover this // if we're already in a fail state, return fail, nothing can recover this
if (testing._status === 'fail') return 'fail'; if (testing._status === 'fail') return 'fail';
if (testing._isSecondWait) {
for (const pw of (testing._onPageWait)) {
testing._captured = pw[1];
pw[0]();
testing._captured = null;
}
}
// run any eventually's that we've captured // run any eventually's that we've captured
for (const ev of testing._eventually) { for (const ev of testing._eventually) {
testing._captured = ev[1]; testing._captured = ev[1];
@@ -101,18 +93,6 @@
_registerErrorCallback(); _registerErrorCallback();
} }
// Set expectations to happen on the next time that `page.wait` is executed.
//
// History specifically uses this as it queues navigation that needs to be checked
// when the next page is loaded.
function onPageWait(fn) {
// Store callbacks to run when page.wait() happens
testing._onPageWait.push([fn, {
script_id: document.currentScript.id,
stack: new Error().stack,
}]);
}
async function async(promise, cb) { async function async(promise, cb) {
const script_id = document.currentScript ? document.currentScript.id : '<script id is unavailable in browsers>'; const script_id = document.currentScript ? document.currentScript.id : '<script id is unavailable in browsers>';
const stack = new Error().stack; const stack = new Error().stack;
@@ -192,15 +172,12 @@
window.testing = { window.testing = {
_status: 'empty', _status: 'empty',
_eventually: [], _eventually: [],
_onPageWait: [],
_executed_scripts: new Set(), _executed_scripts: new Set(),
_captured: null, _captured: null,
_isSecondWait: false,
skip: skip, skip: skip,
async: async, async: async,
getStatus: getStatus, getStatus: getStatus,
eventually: eventually, eventually: eventually,
onPageWait: onPageWait,
expectEqual: expectEqual, expectEqual: expectEqual,
expectError: expectError, expectError: expectError,
withError: withError, withError: withError,