diff --git a/.github/workflows/data-refresh-validation.yaml b/.github/workflows/data-refresh-validation.yaml
index 026d9ee8..58e43bdf 100644
--- a/.github/workflows/data-refresh-validation.yaml
+++ b/.github/workflows/data-refresh-validation.yaml
@@ -15,10 +15,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: "22"
cache: "npm"
diff --git a/.github/workflows/data-refresh.yaml b/.github/workflows/data-refresh.yaml
index 7492f92c..05d92aad 100644
--- a/.github/workflows/data-refresh.yaml
+++ b/.github/workflows/data-refresh.yaml
@@ -21,10 +21,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: "22"
cache: "npm"
@@ -69,10 +69,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: "22"
cache: "npm"
diff --git a/src/urls.ts b/src/urls.ts
index c584b809..d53bef55 100644
--- a/src/urls.ts
+++ b/src/urls.ts
@@ -122,7 +122,7 @@ export const getDownloadUrls = ({
};
export const getUserProfileUrl = ({ username }: { username: string }) =>
- new URL(`/users/${encodeURI(username)}/profile`, getArchiveBaseUrl()).href;
+ new URL(`/users/${encodeURIComponent(username)}/profile`, getArchiveBaseUrl()).href;
const USER_WORKS_SORT_COLUMNS: Record = {
authors: "authors_to_sort_on",
@@ -145,11 +145,11 @@ export const getUserWorksUrl = ({
}: Partial & { username: string; pseud?: string }) => {
// Pseud names are only unique within an account, so AO3 nests them under
// each user.
- const userPath = `/users/${encodeURI(username)}`;
+ const userPath = `/users/${encodeURIComponent(username)}`;
const url = new URL(
pseud === undefined
? `${userPath}/works`
- : `${userPath}/pseuds/${encodeURI(pseud)}/works`,
+ : `${userPath}/pseuds/${encodeURIComponent(pseud)}/works`,
getArchiveBaseUrl(),
);
@@ -202,11 +202,11 @@ const REPLACE_TOKENS_REGEX = new RegExp(
export const getTagUrl = (tagName: string) =>
new URL(
- `tags/${encodeURI(tagName).replaceAll(
+ `tags/${encodeURIComponent(tagName.replaceAll(
REPLACE_TOKENS_REGEX,
(char: string) =>
isReplaceableToken(char) ? TOKEN_REPLACEMENTS_MAP[char] : char,
- )}/`,
+ ))}/`,
getArchiveBaseUrl(),
).href;
diff --git a/tests/helpers/invariants.ts b/tests/helpers/invariants.ts
index a4c1c003..66b377f9 100644
--- a/tests/helpers/invariants.ts
+++ b/tests/helpers/invariants.ts
@@ -1,10 +1,38 @@
import { expect } from "vitest";
+import { prettify } from "htmlfy";
+
+export function expectHtml(html: string | null) {
+ return expect(html === null ? null : prettify(html));
+}
interface DriftingCountMatchers {
driftingCount(bounds: { atLeast: number; atMost: number }): R;
}
+interface HtmlMatchers {
+ toEqualHtml(expected: string | null): R;
+}
+
expect.extend({
+ toEqualHtml(received: unknown, expected: string | null) {
+ if (
+ (received !== null && typeof received !== "string") ||
+ (expected !== null && typeof expected !== "string")
+ ) {
+ throw new TypeError("toEqualHtml requires HTML strings or null");
+ }
+
+ const actualHtml = received === null ? null : prettify(received);
+ const expectedHtml = expected === null ? null : prettify(expected);
+
+ return {
+ pass: actualHtml === expectedHtml,
+ message: () =>
+ `expected HTML ${this.isNot ? "not " : ""}to match after formatting`,
+ actual: actualHtml,
+ expected: expectedHtml,
+ };
+ },
driftingCount(
received: unknown,
bounds: Parameters[0],
@@ -28,6 +56,8 @@ expect.extend({
});
declare module "vitest" {
- interface Assertion extends DriftingCountMatchers {}
- interface AsymmetricMatchersContaining extends DriftingCountMatchers {}
+ interface Assertion
+ extends DriftingCountMatchers, HtmlMatchers {}
+ interface AsymmetricMatchersContaining
+ extends DriftingCountMatchers, HtmlMatchers {}
}
diff --git a/tests/mocks/data/ao3/series/1728802/index.html b/tests/mocks/data/ao3/series/1728802/index.html
index 1add3619..5d452e43 100644
--- a/tests/mocks/data/ao3/series/1728802/index.html
+++ b/tests/mocks/data/ao3/series/1728802/index.html
@@ -34,7 +34,7 @@
-
+
@@ -57,7 +57,7 @@
@@ -445,13 +445,13 @@ Series
Kudos:
- 1,139
+ 1,173
Bookmarks:
- 119
+ 122
Hits:
- 11,909
+ 12,211
@@ -492,13 +492,14 @@ Contact Us
Development