Skip to content

Commit ae1ca60

Browse files
heskewclaude
authored andcommitted
Consolidate static urlPath integration coverage into one file (review)
Both suites test the static component's mount behavior — organize by component, not by issue. Fixtures stay separate: a mount lives in the fixture app's config.yaml and an app has a single static block, so each mount shape needs its own app instance. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent ab8e61e commit ae1ca60

2 files changed

Lines changed: 67 additions & 76 deletions

File tree

integrationTests/components/static-root-urlpath.test.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

integrationTests/components/static-urlpath.test.ts

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
/**
2-
* #1583 — the static plugin served nothing when `urlPath` was configured: the routing chain
3-
* strips the mount prefix from req.pathname while the file map was keyed by the full entry
4-
* URL path, and a slash-less `urlPath` (e.g. 'assets') never matched the route at all.
2+
* Static plugin `urlPath` mount coverage. One suite per mount shape — a mount lives in the
3+
* fixture app's config.yaml and an app has a single `static` block, so each shape needs its
4+
* own fixture/instance:
55
*
6-
* The fixture is the issue's minimal repro: `static: { files: 'web/**', urlPath: 'assets' }`.
6+
* - Subpath mount (#1583): the plugin served nothing when `urlPath` was configured — the
7+
* routing chain strips the mount prefix from req.pathname while the file map was keyed by
8+
* the full entry URL path, and a slash-less `urlPath` (e.g. 'assets') never matched the
9+
* route at all. Fixture: `static: { files: 'web/**', urlPath: 'assets' }`.
10+
* - Root mount (#1766): `urlPath: '/'` matched only the exact path '/' — normalizeUrlPath
11+
* kept '/' as a truthy route constraint, so the handler became a sub-route whose
12+
* segment-boundary check ('/' + '/' = '//') never matched any sub-path; every asset 404'd.
13+
* Fixture: `static: { files: 'web/**', urlPath: '/' }`.
714
*
815
* Reproduction:
916
* npm run test:integration -- "integrationTests/components/static-urlpath.test.ts"
@@ -14,6 +21,7 @@ import { resolve } from 'node:path';
1421
import { setupHarperWithFixture, teardownHarper, type ContextWithHarper } from '@harperfast/integration-testing';
1522

1623
const FIXTURE_PATH = resolve(import.meta.dirname, '../fixtures/static-urlpath');
24+
const ROOT_FIXTURE_PATH = resolve(import.meta.dirname, '../fixtures/static-root-urlpath');
1725

1826
suite('static plugin with urlPath (#1583)', (ctx: ContextWithHarper) => {
1927
before(async () => {
@@ -71,3 +79,58 @@ suite('static plugin with urlPath (#1583)', (ctx: ContextWithHarper) => {
7179
strictEqual(dir.headers.get('location'), '/assets/docs/?foo=bar');
7280
});
7381
});
82+
83+
suite('static plugin with root urlPath (#1766)', (ctx: ContextWithHarper) => {
84+
before(async () => {
85+
await setupHarperWithFixture(ctx, ROOT_FIXTURE_PATH);
86+
});
87+
88+
after(async () => {
89+
await teardownHarper(ctx);
90+
});
91+
92+
test('serves the root index at /', async () => {
93+
const res = await fetch(new URL('/', ctx.harper.httpURL));
94+
const text = await res.text();
95+
strictEqual(res.status, 200, `expected / to serve the root index: ${res.status} ${text}`);
96+
ok(text.includes('root index'));
97+
});
98+
99+
test('serves a file at a root-mounted sub-path', async () => {
100+
const res = await fetch(new URL('/test.css', ctx.harper.httpURL));
101+
const text = await res.text();
102+
strictEqual(res.status, 200, `expected /test.css to serve: ${res.status} ${text}`);
103+
ok(text.includes('teal'), 'served the fixture css content');
104+
});
105+
106+
test('serves the index file by its full path', async () => {
107+
const res = await fetch(new URL('/index.html', ctx.harper.httpURL));
108+
const text = await res.text();
109+
strictEqual(res.status, 200, `expected /index.html to serve: ${res.status} ${text}`);
110+
ok(text.includes('root index'));
111+
});
112+
113+
test('serves a nested directory index', async () => {
114+
const res = await fetch(new URL('/docs/', ctx.harper.httpURL));
115+
const text = await res.text();
116+
strictEqual(res.status, 200, `expected /docs/ to serve index.html: ${res.status} ${text}`);
117+
ok(text.includes('docs index'));
118+
});
119+
120+
test('redirects a directory request to the trailing-slash path', async () => {
121+
const res = await fetch(new URL('/docs', ctx.harper.httpURL), { redirect: 'manual' });
122+
strictEqual(res.status, 301);
123+
strictEqual(res.headers.get('location'), '/docs/');
124+
});
125+
126+
test('preserves the query string on the trailing-slash redirect', async () => {
127+
const res = await fetch(new URL('/docs?foo=bar', ctx.harper.httpURL), { redirect: 'manual' });
128+
strictEqual(res.status, 301);
129+
strictEqual(res.headers.get('location'), '/docs/?foo=bar');
130+
});
131+
132+
test('falls through to a 404 for paths with no static entry', async () => {
133+
const res = await fetch(new URL('/no-such-file.js', ctx.harper.httpURL));
134+
strictEqual(res.status, 404);
135+
});
136+
});

0 commit comments

Comments
 (0)