mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
migrate fetch tests to htmlRunner
This commit is contained in:
@@ -17,9 +17,12 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const log = @import("../../log.zig");
|
||||
const URL = @import("../../url.zig").URL;
|
||||
const Page = @import("../page.zig").Page;
|
||||
|
||||
const iterator = @import("../iterator/iterator.zig");
|
||||
|
||||
const v8 = @import("v8");
|
||||
const Env = @import("../env.zig").Env;
|
||||
|
||||
@@ -108,7 +111,8 @@ pub fn constructor(_init: ?HeadersInit, page: *Page) !Headers {
|
||||
}
|
||||
|
||||
pub fn append(self: *Headers, name: []const u8, value: []const u8, allocator: std.mem.Allocator) !void {
|
||||
const gop = try self.headers.getOrPut(allocator, name);
|
||||
const key = try allocator.dupe(u8, name);
|
||||
const gop = try self.headers.getOrPut(allocator, key);
|
||||
|
||||
if (gop.found_existing) {
|
||||
// If we found it, append the value.
|
||||
@@ -129,13 +133,13 @@ pub fn _delete(self: *Headers, name: []const u8) void {
|
||||
_ = self.headers.remove(name);
|
||||
}
|
||||
|
||||
pub const HeaderEntryIterator = struct {
|
||||
pub const HeadersEntryIterator = struct {
|
||||
slot: [2][]const u8,
|
||||
iter: HeaderHashMap.Iterator,
|
||||
|
||||
// TODO: these SHOULD be in lexigraphical order but I'm not sure how actually
|
||||
// important that is.
|
||||
pub fn _next(self: *HeaderEntryIterator) ?[2][]const u8 {
|
||||
pub fn _next(self: *HeadersEntryIterator) ?[2][]const u8 {
|
||||
if (self.iter.next()) |entry| {
|
||||
self.slot[0] = entry.key_ptr.*;
|
||||
self.slot[1] = entry.value_ptr.*;
|
||||
@@ -146,10 +150,12 @@ pub const HeaderEntryIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn _entries(self: *const Headers) HeaderEntryIterator {
|
||||
pub fn _entries(self: *const Headers) HeadersEntryIterable {
|
||||
return .{
|
||||
.slot = undefined,
|
||||
.iter = self.headers.iterator(),
|
||||
.inner = .{
|
||||
.slot = undefined,
|
||||
.iter = self.headers.iterator(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -171,10 +177,10 @@ pub fn _has(self: *const Headers, name: []const u8) bool {
|
||||
return self.headers.contains(name);
|
||||
}
|
||||
|
||||
pub const HeaderKeyIterator = struct {
|
||||
pub const HeadersKeyIterator = struct {
|
||||
iter: HeaderHashMap.KeyIterator,
|
||||
|
||||
pub fn _next(self: *HeaderKeyIterator) ?[]const u8 {
|
||||
pub fn _next(self: *HeadersKeyIterator) ?[]const u8 {
|
||||
if (self.iter.next()) |key| {
|
||||
return key.*;
|
||||
} else {
|
||||
@@ -183,21 +189,22 @@ pub const HeaderKeyIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn _keys(self: *const Headers) HeaderKeyIterator {
|
||||
return .{ .iter = self.headers.keyIterator() };
|
||||
pub fn _keys(self: *const Headers) HeadersKeyIterable {
|
||||
return .{ .inner = .{ .iter = self.headers.keyIterator() } };
|
||||
}
|
||||
|
||||
pub fn _set(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void {
|
||||
const arena = page.arena;
|
||||
|
||||
const gop = try self.headers.getOrPut(arena, name);
|
||||
const key = try arena.dupe(u8, name);
|
||||
const gop = try self.headers.getOrPut(arena, key);
|
||||
gop.value_ptr.* = try arena.dupe(u8, value);
|
||||
}
|
||||
|
||||
pub const HeaderValueIterator = struct {
|
||||
pub const HeadersValueIterator = struct {
|
||||
iter: HeaderHashMap.ValueIterator,
|
||||
|
||||
pub fn _next(self: *HeaderValueIterator) ?[]const u8 {
|
||||
pub fn _next(self: *HeadersValueIterator) ?[]const u8 {
|
||||
if (self.iter.next()) |value| {
|
||||
return value.*;
|
||||
} else {
|
||||
@@ -206,53 +213,15 @@ pub const HeaderValueIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn _values(self: *const Headers) HeaderValueIterator {
|
||||
return .{ .iter = self.headers.valueIterator() };
|
||||
pub fn _values(self: *const Headers) HeadersValueIterable {
|
||||
return .{ .inner = .{ .iter = self.headers.valueIterator() } };
|
||||
}
|
||||
|
||||
pub const HeadersKeyIterable = iterator.Iterable(HeadersKeyIterator, "HeadersKeyIterator");
|
||||
pub const HeadersValueIterable = iterator.Iterable(HeadersValueIterator, "HeadersValueIterator");
|
||||
pub const HeadersEntryIterable = iterator.Iterable(HeadersEntryIterator, "HeadersEntryIterator");
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "fetch: headers" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io" });
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "let emptyHeaders = new Headers()", "undefined" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "let headers = new Headers({'Set-Cookie': 'name=world'})", "undefined" },
|
||||
.{ "headers.get('set-cookie')", "name=world" },
|
||||
}, .{});
|
||||
|
||||
// adapted from the mdn examples
|
||||
try runner.testCases(&.{
|
||||
.{ "const myHeaders = new Headers();", "undefined" },
|
||||
.{ "myHeaders.append('Content-Type', 'image/jpeg')", "undefined" },
|
||||
.{ "myHeaders.has('Picture-Type')", "false" },
|
||||
.{ "myHeaders.get('Content-Type')", "image/jpeg" },
|
||||
.{ "myHeaders.append('Content-Type', 'image/png')", "undefined" },
|
||||
.{ "myHeaders.get('Content-Type')", "image/jpeg, image/png" },
|
||||
.{ "myHeaders.delete('Content-Type')", "undefined" },
|
||||
.{ "myHeaders.get('Content-Type')", "null" },
|
||||
.{ "myHeaders.set('Picture-Type', 'image/svg')", "undefined" },
|
||||
.{ "myHeaders.get('Picture-Type')", "image/svg" },
|
||||
.{ "myHeaders.has('Picture-Type')", "true" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "const originalHeaders = new Headers([['Content-Type', 'application/json'], ['Authorization', 'Bearer token123']])", "undefined" },
|
||||
.{ "originalHeaders.get('Content-Type')", "application/json" },
|
||||
.{ "originalHeaders.get('Authorization')", "Bearer token123" },
|
||||
.{ "const newHeaders = new Headers(originalHeaders)", "undefined" },
|
||||
.{ "newHeaders.get('Content-Type')", "application/json" },
|
||||
.{ "newHeaders.get('Authorization')", "Bearer token123" },
|
||||
.{ "newHeaders.has('Content-Type')", "true" },
|
||||
.{ "newHeaders.has('Authorization')", "true" },
|
||||
.{ "newHeaders.has('X-Custom')", "false" },
|
||||
// Verify that modifying the new headers doesn't affect the original
|
||||
.{ "newHeaders.set('X-Custom', 'test-value')", "undefined" },
|
||||
.{ "newHeaders.get('X-Custom')", "test-value" },
|
||||
.{ "originalHeaders.get('X-Custom')", "null" },
|
||||
.{ "originalHeaders.has('X-Custom')", "false" },
|
||||
}, .{});
|
||||
test "fetch: Headers" {
|
||||
try testing.htmlRunner("fetch/headers.html");
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ pub const RequestCache = enum {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toString(self: RequestCache) []const u8 {
|
||||
return @tagName(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const RequestCredentials = enum {
|
||||
@@ -70,6 +74,10 @@ pub const RequestCredentials = enum {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toString(self: RequestCredentials) []const u8 {
|
||||
return @tagName(self);
|
||||
}
|
||||
};
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
|
||||
@@ -154,6 +162,10 @@ pub fn get_cache(self: *const Request) RequestCache {
|
||||
return self.cache;
|
||||
}
|
||||
|
||||
pub fn get_credentials(self: *const Request) RequestCredentials {
|
||||
return self.credentials;
|
||||
}
|
||||
|
||||
pub fn get_headers(self: *Request) *Headers {
|
||||
return &self.headers;
|
||||
}
|
||||
@@ -249,50 +261,6 @@ pub fn _text(self: *Response, page: *Page) !Env.Promise {
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "fetch: request" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io" });
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "let request = new Request('flower.png')", "undefined" },
|
||||
.{ "request.url", "https://lightpanda.io/flower.png" },
|
||||
.{ "request.method", "GET" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "let request2 = new Request('https://google.com', { method: 'POST', body: 'Hello, World' })", "undefined" },
|
||||
.{ "request2.url", "https://google.com" },
|
||||
.{ "request2.method", "POST" },
|
||||
}, .{});
|
||||
}
|
||||
|
||||
test "fetch: Browser.fetch" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{
|
||||
\\ var ok = false;
|
||||
\\ const request = new Request("http://127.0.0.1:9582/loader");
|
||||
\\ fetch(request).then((response) => { ok = response.ok; });
|
||||
\\ false;
|
||||
,
|
||||
"false",
|
||||
},
|
||||
// all events have been resolved.
|
||||
.{ "ok", "true" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{
|
||||
\\ var ok2 = false;
|
||||
\\ const request2 = new Request("http://127.0.0.1:9582/loader");
|
||||
\\ (async function () { resp = await fetch(request2); ok2 = resp.ok; }());
|
||||
\\ false;
|
||||
,
|
||||
"false",
|
||||
},
|
||||
// all events have been resolved.
|
||||
.{ "ok2", "true" },
|
||||
}, .{});
|
||||
test "fetch: Request" {
|
||||
try testing.htmlRunner("fetch/request.html");
|
||||
}
|
||||
|
||||
@@ -36,7 +36,8 @@ const Page = @import("../page.zig").Page;
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response
|
||||
const Response = @This();
|
||||
|
||||
status: u16 = 0,
|
||||
status: u16 = 200,
|
||||
status_text: []const u8 = "",
|
||||
headers: Headers,
|
||||
mime: ?Mime = null,
|
||||
url: []const u8 = "",
|
||||
@@ -50,7 +51,7 @@ const ResponseBody = union(enum) {
|
||||
|
||||
const ResponseOptions = struct {
|
||||
status: u16 = 200,
|
||||
statusText: []const u8 = "",
|
||||
statusText: ?[]const u8 = null,
|
||||
headers: ?HeadersInit = null,
|
||||
};
|
||||
|
||||
@@ -72,10 +73,13 @@ pub fn constructor(_input: ?ResponseBody, _options: ?ResponseOptions, page: *Pag
|
||||
};
|
||||
|
||||
const headers: Headers = if (options.headers) |hdrs| try Headers.constructor(hdrs, page) else .{};
|
||||
const status_text = if (options.statusText) |st| try arena.dupe(u8, st) else "";
|
||||
|
||||
return .{
|
||||
.body = body,
|
||||
.headers = headers,
|
||||
.status = options.status,
|
||||
.status_text = status_text,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -105,6 +109,10 @@ pub fn get_status(self: *const Response) u16 {
|
||||
return self.status;
|
||||
}
|
||||
|
||||
pub fn get_statusText(self: *const Response) []const u8 {
|
||||
return self.status_text;
|
||||
}
|
||||
|
||||
pub fn get_url(self: *const Response) []const u8 {
|
||||
return self.url;
|
||||
}
|
||||
@@ -183,9 +191,6 @@ pub fn _text(self: *Response, page: *Page) !Env.Promise {
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "fetch: response" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io" });
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{}, .{});
|
||||
test "fetch: Response" {
|
||||
try testing.htmlRunner("fetch/response.html");
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ const Response = @import("Response.zig");
|
||||
|
||||
pub const Interfaces = .{
|
||||
@import("Headers.zig"),
|
||||
@import("Headers.zig").HeaderEntryIterator,
|
||||
@import("Headers.zig").HeaderKeyIterator,
|
||||
@import("Headers.zig").HeaderValueIterator,
|
||||
@import("Headers.zig").HeadersEntryIterable,
|
||||
@import("Headers.zig").HeadersKeyIterable,
|
||||
@import("Headers.zig").HeadersValueIterable,
|
||||
@import("Request.zig"),
|
||||
@import("Response.zig"),
|
||||
};
|
||||
|
||||
102
src/tests/fetch/headers.html
Normal file
102
src/tests/fetch/headers.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=headers>
|
||||
let headers = new Headers({"Set-Cookie": "name=world"});
|
||||
testing.expectEqual("name=world", headers.get("set-cookie"));
|
||||
|
||||
let myHeaders = new Headers();
|
||||
myHeaders.append("Content-Type", "image/jpeg"),
|
||||
testing.expectEqual(false, myHeaders.has("Picture-Type"));
|
||||
testing.expectEqual("image/jpeg", myHeaders.get("Content-Type"));
|
||||
|
||||
myHeaders.append("Content-Type", "image/png");
|
||||
testing.expectEqual("image/jpeg, image/png", myHeaders.get("Content-Type"));
|
||||
|
||||
myHeaders.delete("Content-Type");
|
||||
testing.expectEqual(null, myHeaders.get("Content-Type"));
|
||||
|
||||
myHeaders.set("Picture-Type", "image/svg")
|
||||
testing.expectEqual("image/svg", myHeaders.get("Picture-Type"));
|
||||
testing.expectEqual(true, myHeaders.has("Picture-Type"))
|
||||
|
||||
const originalHeaders = new Headers([["Content-Type", "application/json"], ["Authorization", "Bearer token123"]]);
|
||||
testing.expectEqual("application/json", originalHeaders.get("Content-Type"));
|
||||
testing.expectEqual("Bearer token123", originalHeaders.get("Authorization"));
|
||||
|
||||
const newHeaders = new Headers(originalHeaders);
|
||||
testing.expectEqual("application/json", newHeaders.get("Content-Type"));
|
||||
testing.expectEqual("Bearer token123" ,newHeaders.get("Authorization"));
|
||||
testing.expectEqual(true ,newHeaders.has("Content-Type"));
|
||||
testing.expectEqual(true ,newHeaders.has("Authorization"));
|
||||
testing.expectEqual(false, newHeaders.has("X-Custom"));
|
||||
|
||||
newHeaders.set("X-Custom", "test-value");
|
||||
testing.expectEqual("test-value", newHeaders.get("X-Custom"));
|
||||
testing.expectEqual(null, originalHeaders.get("X-Custom"));
|
||||
testing.expectEqual(false, originalHeaders.has("X-Custom"));
|
||||
</script>
|
||||
|
||||
<script id=keys>
|
||||
const testKeyHeaders = new Headers();
|
||||
testKeyHeaders.set("Content-Type", "application/json");
|
||||
testKeyHeaders.set("Authorization", "Bearer token123");
|
||||
testKeyHeaders.set("X-Custom", "test-value");
|
||||
|
||||
const keys = [];
|
||||
for (const key of testKeyHeaders.keys()) {
|
||||
keys.push(key);
|
||||
}
|
||||
|
||||
testing.expectEqual(3, keys.length);
|
||||
testing.expectEqual(true, keys.includes("Content-Type"));
|
||||
testing.expectEqual(true, keys.includes("Authorization"));
|
||||
testing.expectEqual(true, keys.includes("X-Custom"));
|
||||
</script>
|
||||
|
||||
<script id=values>
|
||||
const testValuesHeaders = new Headers();
|
||||
testValuesHeaders.set("Content-Type", "application/json");
|
||||
testValuesHeaders.set("Authorization", "Bearer token123");
|
||||
testValuesHeaders.set("X-Custom", "test-value");
|
||||
|
||||
const values = [];
|
||||
for (const value of testValuesHeaders.values()) {
|
||||
values.push(value);
|
||||
}
|
||||
|
||||
testing.expectEqual(3, values.length);
|
||||
testing.expectEqual(true, values.includes("application/json"));
|
||||
testing.expectEqual(true, values.includes("Bearer token123"));
|
||||
testing.expectEqual(true, values.includes("test-value"));
|
||||
</script>
|
||||
|
||||
<script id=entries>
|
||||
const testEntriesHeaders = new Headers();
|
||||
testEntriesHeaders.set("Content-Type", "application/json");
|
||||
testEntriesHeaders.set("Authorization", "Bearer token123");
|
||||
testEntriesHeaders.set("X-Custom", "test-value");
|
||||
|
||||
const entries = [];
|
||||
for (const entry of testEntriesHeaders.entries()) {
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
testing.expectEqual(3, entries.length);
|
||||
|
||||
const entryMap = new Map(entries);
|
||||
testing.expectEqual("application/json", entryMap.get("Content-Type"));
|
||||
testing.expectEqual("Bearer token123", entryMap.get("Authorization"));
|
||||
testing.expectEqual("test-value", entryMap.get("X-Custom"));
|
||||
|
||||
const entryKeys = Array.from(entryMap.keys());
|
||||
testing.expectEqual(3, entryKeys.length);
|
||||
testing.expectEqual(true, entryKeys.includes("Content-Type"));
|
||||
testing.expectEqual(true, entryKeys.includes("Authorization"));
|
||||
testing.expectEqual(true, entryKeys.includes("X-Custom"));
|
||||
|
||||
const entryValues = Array.from(entryMap.values());
|
||||
testing.expectEqual(3, entryValues.length);
|
||||
testing.expectEqual(true, entryValues.includes("application/json"));
|
||||
testing.expectEqual(true, entryValues.includes("Bearer token123"));
|
||||
testing.expectEqual(true, entryValues.includes("test-value"))
|
||||
</script>
|
||||
22
src/tests/fetch/request.html
Normal file
22
src/tests/fetch/request.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=request>
|
||||
let request = new Request("flower.png");
|
||||
testing.expectEqual("http://localhost:9582/src/tests/fetch/flower.png", request.url);
|
||||
testing.expectEqual("GET", request.method);
|
||||
|
||||
let request2 = new Request("https://google.com", {
|
||||
method: "POST",
|
||||
body: "Hello, World",
|
||||
cache: "reload",
|
||||
credentials: "omit",
|
||||
headers: { "Sender": "me", "Target": "you" }
|
||||
}
|
||||
);
|
||||
testing.expectEqual("https://google.com", request2.url);
|
||||
testing.expectEqual("POST", request2.method);
|
||||
testing.expectEqual("omit", request2.credentials);
|
||||
testing.expectEqual("reload", request2.cache);
|
||||
testing.expectEqual("me", request2.headers.get("SeNdEr"));
|
||||
testing.expectEqual("you", request2.headers.get("target"));
|
||||
</script>
|
||||
38
src/tests/fetch/response.html
Normal file
38
src/tests/fetch/response.html
Normal file
@@ -0,0 +1,38 @@
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=response>
|
||||
let response = new Response("Hello, World!");
|
||||
testing.expectEqual(200, response.status);
|
||||
testing.expectEqual("", response.statusText);
|
||||
testing.expectEqual(true, response.ok);
|
||||
testing.expectEqual("", response.url);
|
||||
testing.expectEqual(false, response.redirected);
|
||||
|
||||
let response2 = new Response("Error occurred", {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
headers: {
|
||||
"Content-Type": "text/plain",
|
||||
"X-Custom": "test-value",
|
||||
"Cache-Control": "no-cache"
|
||||
}
|
||||
});
|
||||
testing.expectEqual(404, response2.status);
|
||||
testing.expectEqual("Not Found", response2.statusText);
|
||||
testing.expectEqual(false, response2.ok);
|
||||
testing.expectEqual("text/plain", response2.headers.get("Content-Type"));
|
||||
testing.expectEqual("test-value", response2.headers.get("X-Custom"));
|
||||
testing.expectEqual("no-cache", response2.headers.get("cache-control"));
|
||||
|
||||
let response3 = new Response("Created", { status: 201, statusText: "Created" });
|
||||
testing.expectEqual(201, response3.status);
|
||||
testing.expectEqual("Created", response3.statusText);
|
||||
testing.expectEqual(true, response3.ok);
|
||||
|
||||
let nullResponse = new Response(null);
|
||||
testing.expectEqual(200, nullResponse.status);
|
||||
testing.expectEqual("", nullResponse.statusText);
|
||||
|
||||
let emptyResponse = new Response("");
|
||||
testing.expectEqual(200, emptyResponse.status);
|
||||
</script>
|
||||
Reference in New Issue
Block a user