mirror of
https://github.com/withastro/astro.git
synced 2025-03-24 23:21:57 -05:00
* feat(i18n): expand fallback system * rebase * apply feedback * better changeset * update tests too * apply feedback * Update .changeset/blue-pens-divide.md Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> * update docs * nitpick * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/blue-pens-divide.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * Update .changeset/blue-pens-divide.md Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * fix regression --------- Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
576 lines
16 KiB
JavaScript
576 lines
16 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { after, before, describe, it } from 'node:test';
|
|
import { load as cheerioLoad } from 'cheerio';
|
|
import testAdapter from './test-adapter.js';
|
|
import { loadFixture } from './test-utils.js';
|
|
|
|
describe('Dev reroute', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/reroute/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should render the index page when navigating /reroute ', async () => {
|
|
const html = await fixture.fetch('/reroute').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating /blog/hello ', async () => {
|
|
const html = await fixture.fetch('/blog/hello').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating /blog/salut ', async () => {
|
|
const html = await fixture.fetch('/blog/salut').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating dynamic route /dynamic/[id] ', async () => {
|
|
const html = await fixture.fetch('/dynamic/hello').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating spread route /spread/[...spread] ', async () => {
|
|
const html = await fixture.fetch('/spread/hello').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should return a 404', async () => {
|
|
const response = await fixture.fetch('/blog/oops');
|
|
|
|
assert.equal(response.status, 404);
|
|
});
|
|
});
|
|
|
|
describe('Dev rewrite, trailing slash -> never', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-trailing-slash-never/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should rewrite to the homepage', async () => {
|
|
const html = await fixture.fetch('/foo').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
});
|
|
|
|
describe('Dev rewrite, trailing slash -> never, with base', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-trailing-slash-never/',
|
|
base: 'base',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should rewrite to the homepage', async () => {
|
|
const html = await fixture.fetch('/base/foo').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
});
|
|
|
|
describe('Dev rewrite, hybrid/server', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-server/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should rewrite the [slug]/title ', async () => {
|
|
const html = await fixture.fetch('/').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.match($('h1').text(), /Title/);
|
|
assert.match($('p').text(), /some-slug/);
|
|
});
|
|
|
|
it('should display an error if a rewrite is attempted after the body has been consumed', async () => {
|
|
const formData = new FormData();
|
|
formData.append('email', 'example@example.com');
|
|
|
|
const request = new Request('http://example.com/post/post-body-used', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
const response = await fixture.fetch('/post/post-body-used', request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('title').text(), 'RewriteWithBodyUsed');
|
|
});
|
|
});
|
|
|
|
describe('Build reroute', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/reroute/',
|
|
});
|
|
await fixture.build();
|
|
});
|
|
|
|
it('should create the index page when navigating /reroute ', async () => {
|
|
const html = await fixture.readFile('/reroute/index.html');
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should create the index page when navigating /blog/hello ', async () => {
|
|
const html = await fixture.readFile('/blog/hello/index.html');
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should create the index page when navigating /blog/salut ', async () => {
|
|
const html = await fixture.readFile('/blog/salut/index.html');
|
|
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should create the index page when navigating dynamic route /dynamic/[id] ', async () => {
|
|
const html = await fixture.readFile('/dynamic/hello/index.html');
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should create the index page when navigating spread route /spread/[...spread] ', async () => {
|
|
const html = await fixture.readFile('/spread/hello/index.html');
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should create the 404 built-in page', async () => {
|
|
try {
|
|
await fixture.readFile('/spread/oops/index.html');
|
|
assert.fail('Not found');
|
|
} catch {
|
|
assert.ok;
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('SSR route', () => {
|
|
it("should not build if a user tries to use rewrite('/404') in static pages", async () => {
|
|
try {
|
|
const fixture = await loadFixture({
|
|
root: './fixtures/rewrite-404-invalid/',
|
|
});
|
|
await fixture.build();
|
|
assert.fail('It should fail.');
|
|
} catch {
|
|
// it passes
|
|
assert.equal(true, true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('SSR reroute', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let app;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/reroute/',
|
|
output: 'server',
|
|
adapter: testAdapter(),
|
|
});
|
|
await fixture.build();
|
|
app = await fixture.loadTestAdapterApp();
|
|
});
|
|
|
|
it('should render the index page when navigating /reroute ', async () => {
|
|
const request = new Request('http://example.com/reroute');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating /blog/hello ', async () => {
|
|
const request = new Request('http://example.com/blog/hello');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating /blog/salut ', async () => {
|
|
const request = new Request('http://example.com/blog/salut');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating dynamic route /dynamic/[id] ', async () => {
|
|
const request = new Request('http://example.com/dynamic/hello');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the index page when navigating spread route /spread/[...spread] ', async () => {
|
|
const request = new Request('http://example.com/spread/hello');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
});
|
|
|
|
it('should render the 404 built-in page', async () => {
|
|
const request = new Request('http://example.com/blog/oops');
|
|
const response = await app.render(request);
|
|
assert.equal(response.status, 404);
|
|
});
|
|
|
|
it('should pass the POST data from one page to another', async () => {
|
|
const request = new Request('http://example.com/post/post-a', {
|
|
method: 'POST',
|
|
body: JSON.stringify({
|
|
email: 'example@example.com',
|
|
}),
|
|
headers: {
|
|
'content-type': 'application/json',
|
|
},
|
|
});
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Post B');
|
|
assert.match($('h2').text(), /example@example.com/);
|
|
});
|
|
});
|
|
|
|
describe('SSR rewrite, hybrid/server', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let app;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-server/',
|
|
output: 'server',
|
|
adapter: testAdapter(),
|
|
});
|
|
|
|
await fixture.build();
|
|
app = await fixture.loadTestAdapterApp();
|
|
});
|
|
|
|
it('should rewrite the [slug]/title ', async () => {
|
|
const request = new Request('http://example.com/');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
const $ = cheerioLoad(html);
|
|
|
|
console.log(html);
|
|
|
|
assert.match($('h1').text(), /Title/);
|
|
assert.match($('p').text(), /some-slug/);
|
|
});
|
|
|
|
it('should return a 500 if a rewrite is attempted after the body has been read', async () => {
|
|
const formData = new FormData();
|
|
formData.append('email', 'example@example.com');
|
|
|
|
const request = new Request('http://example.com/post/post-body-used', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
const response = await app.render(request);
|
|
assert.equal(response.status, 500);
|
|
});
|
|
});
|
|
|
|
describe('Middleware', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/reroute/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should render a locals populated in the third middleware function, because we use next("/")', async () => {
|
|
const html = await fixture.fetch('/auth/base').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
assert.equal($('p').text(), 'Called auth');
|
|
});
|
|
|
|
it('should NOT render locals populated in the third middleware function, because we use ctx.reroute("/")', async () => {
|
|
const html = await fixture.fetch('/auth/dashboard').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Index');
|
|
assert.equal($('p').text(), '');
|
|
});
|
|
|
|
it('should render the index when rewriting with params', async () => {
|
|
const html = await fixture.fetch('/auth/params').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.match($('h1').text(), /Index/);
|
|
});
|
|
});
|
|
|
|
describe('Middleware with custom 404.astro and 500.astro', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-custom-404/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('The `next()` function should return a Response with status code 404', async () => {
|
|
const html = await fixture.fetch('/about').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Custom error');
|
|
assert.equal($('p').text(), 'Interjected');
|
|
});
|
|
|
|
it('The `next()` function should return a Response with status code 500', async () => {
|
|
const html = await fixture.fetch('/about-2').then((res) => res.text());
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Custom error');
|
|
assert.equal($('p').text(), 'Interjected');
|
|
});
|
|
});
|
|
|
|
describe('Runtime error, default 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-runtime-error/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should return a 500 status code, but not render the custom 500', async () => {
|
|
const response = await fixture.fetch('/errors/from');
|
|
assert.equal(response.status, 500);
|
|
const text = await response.text();
|
|
assert.match(text, /@vite\/client/);
|
|
});
|
|
});
|
|
|
|
describe('Runtime error in SSR, default 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let app;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-runtime-error/',
|
|
output: 'server',
|
|
adapter: testAdapter(),
|
|
});
|
|
await fixture.build();
|
|
app = await fixture.loadTestAdapterApp();
|
|
});
|
|
|
|
it('should return a 500 status code, but not render the custom 500', async () => {
|
|
const request = new Request('http://example.com/errors/from');
|
|
const response = await app.render(request);
|
|
const text = await response.text();
|
|
assert.equal(text, '');
|
|
});
|
|
});
|
|
|
|
describe('Runtime error in dev, custom 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-runtime-error-custom500/',
|
|
});
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should render the custom 500 when rewriting a page that throws an error', async () => {
|
|
const response = await fixture.fetch('/errors/start');
|
|
assert.equal(response.status, 500);
|
|
const html = await response.text();
|
|
assert.match(html, /I am the custom 500/);
|
|
});
|
|
});
|
|
|
|
describe('Runtime error in SSR, custom 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let app;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-runtime-error-custom500/',
|
|
output: 'server',
|
|
adapter: testAdapter(),
|
|
});
|
|
await fixture.build();
|
|
app = await fixture.loadTestAdapterApp();
|
|
});
|
|
|
|
it('should render the custom 500 when rewriting a page that throws an error', async () => {
|
|
const request = new Request('http://example.com/errors/start');
|
|
const response = await app.render(request);
|
|
const html = await response.text();
|
|
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'I am the custom 500');
|
|
});
|
|
});
|
|
|
|
describe('Runtime error in dev, custom 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let devServer;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-i18n-manual-routing/',
|
|
});
|
|
|
|
devServer = await fixture.startDevServer();
|
|
});
|
|
|
|
after(async () => {
|
|
await devServer.stop();
|
|
});
|
|
|
|
it('should return a status 200 when rewriting from the middleware to the homepage', async () => {
|
|
const response = await fixture.fetch('/reroute');
|
|
assert.equal(response.status, 200);
|
|
const html = await response.text();
|
|
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Expected http status of index page is 200');
|
|
});
|
|
});
|
|
|
|
describe('Runtime error in SSR, custom 500', () => {
|
|
/** @type {import('./test-utils').Fixture} */
|
|
let fixture;
|
|
let app;
|
|
|
|
before(async () => {
|
|
fixture = await loadFixture({
|
|
root: './fixtures/rewrite-i18n-manual-routing/',
|
|
output: 'server',
|
|
adapter: testAdapter(),
|
|
});
|
|
await fixture.build();
|
|
app = await fixture.loadTestAdapterApp();
|
|
});
|
|
|
|
it('should return a status 200 when rewriting from the middleware to the homepage', async () => {
|
|
const request = new Request('http://example.com/foo');
|
|
const response = await app.render(request);
|
|
assert.equal(response.status, 200);
|
|
const html = await response.text();
|
|
|
|
const $ = cheerioLoad(html);
|
|
|
|
assert.equal($('h1').text(), 'Expected http status of index page is 200');
|
|
});
|
|
});
|