mirror of
https://github.com/kevin-DL/sapper.git
synced 2026-01-11 10:54:34 +00:00
Harden tests (...a bit)
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,11 +3,7 @@ yarn.lock
|
||||
yarn-error.log
|
||||
node_modules
|
||||
cypress/screenshots
|
||||
test/app/.sapper
|
||||
test/app/src/manifest
|
||||
__sapper__
|
||||
test/app/export
|
||||
test/app/build
|
||||
sapper
|
||||
runtime.js
|
||||
dist
|
||||
|
||||
@@ -1,49 +1,77 @@
|
||||
import * as path from 'path';
|
||||
import puppeteer from 'puppeteer';
|
||||
import * as ports from 'port-authority';
|
||||
import { fork, ChildProcess } from 'child_process';
|
||||
import { AddressInfo } from 'net';
|
||||
|
||||
import { wait } from '../utils'
|
||||
|
||||
const DEFAULT_ENTRY = '__sapper__/build/server/server.js';
|
||||
const DELAY = parseInt(process.env.SAPPER_TEST_DELAY) || 50;
|
||||
|
||||
declare const start: () => Promise<void>;
|
||||
declare const prefetchRoutes: () => Promise<void>;
|
||||
declare const prefetch: (href: string) => Promise<void>;
|
||||
declare const goto: (href: string) => Promise<void>;
|
||||
|
||||
type StartOpts = {
|
||||
requestInterceptor?: (interceptedRequst: puppeteer.Request) => any
|
||||
};
|
||||
|
||||
export class AppRunner {
|
||||
cwd: string;
|
||||
entry: string;
|
||||
port: number;
|
||||
proc: ChildProcess;
|
||||
exiting: boolean;
|
||||
terminate: Promise<any>;
|
||||
|
||||
server: ChildProcess;
|
||||
address: AddressInfo;
|
||||
base: string;
|
||||
messages: any[];
|
||||
errors: Error[];
|
||||
|
||||
browser: puppeteer.Browser;
|
||||
page: puppeteer.Page;
|
||||
|
||||
constructor(cwd: string, entry: string) {
|
||||
this.cwd = cwd;
|
||||
this.entry = path.join(cwd, entry);
|
||||
sapper = {
|
||||
start: () => this.page.evaluate(() => start()).then(() => void 0),
|
||||
prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()).then(() => void 0),
|
||||
prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href).then(() => void 0),
|
||||
goto: (href: string) => this.page.evaluate((href: string) => goto(href), href).then(() => void 0)
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.messages = [];
|
||||
this.errors = [];
|
||||
}
|
||||
|
||||
async start({ requestInterceptor }: StartOpts = {}) {
|
||||
this.port = await ports.find(3000);
|
||||
async start(cwd: string, entry: string = DEFAULT_ENTRY) {
|
||||
const server_listening = deferred();
|
||||
const server_closed = deferred();
|
||||
const browser_closed = deferred();
|
||||
|
||||
this.proc = fork(this.entry, [], {
|
||||
cwd: this.cwd,
|
||||
env: {
|
||||
PORT: String(this.port)
|
||||
this.terminate = Promise.all([server_closed, browser_closed]);
|
||||
|
||||
this.server = fork(path.join(cwd, entry), [], { cwd });
|
||||
this.server.on('exit', () => {
|
||||
server_listening.reject();
|
||||
server_closed.settle(this.exiting);
|
||||
});
|
||||
this.server.on('message', message => {
|
||||
if (!message.__sapper__) return;
|
||||
|
||||
switch (message.event) {
|
||||
case 'listening':
|
||||
this.address = message.address;
|
||||
this.base = `http://localhost:${this.address.port}`;
|
||||
|
||||
server_listening.resolve();
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
this.errors.push(Object.assign(new Error(), message.error));
|
||||
break;
|
||||
|
||||
default:
|
||||
this.messages.push(message);
|
||||
}
|
||||
});
|
||||
|
||||
this.proc.on('message', message => {
|
||||
if (!message.__sapper__) return;
|
||||
this.messages.push(message);
|
||||
});
|
||||
|
||||
this.browser = await puppeteer.launch({ args: ['--no-sandbox'] });
|
||||
this.browser.on('disconnected', () => browser_closed.settle(this.exiting));
|
||||
|
||||
this.page = await this.browser.newPage();
|
||||
this.page.on('console', msg => {
|
||||
@@ -54,25 +82,28 @@ export class AppRunner {
|
||||
}
|
||||
});
|
||||
|
||||
if (requestInterceptor) {
|
||||
await this.page.setRequestInterception(true);
|
||||
this.page.on('request', requestInterceptor);
|
||||
}
|
||||
await server_listening;
|
||||
|
||||
return {
|
||||
page: this.page,
|
||||
base: `http://localhost:${this.port}`,
|
||||
|
||||
// helpers
|
||||
start: () => this.page.evaluate(() => start()).then(() => void 0),
|
||||
prefetchRoutes: () => this.page.evaluate(() => prefetchRoutes()).then(() => void 0),
|
||||
prefetch: (href: string) => this.page.evaluate((href: string) => prefetch(href), href).then(() => void 0),
|
||||
goto: (href: string) => this.page.evaluate((href: string) => goto(href), href).then(() => void 0),
|
||||
title: () => this.page.$eval('h1', node => node.textContent).then(serializable => String(serializable))
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
capture(fn: () => any): Promise<string[]> {
|
||||
load(url: string) {
|
||||
if (url[0] === '/') {
|
||||
url = `${this.base}${url}`;
|
||||
}
|
||||
|
||||
return this.page.goto(url);
|
||||
}
|
||||
|
||||
text(selector: string) {
|
||||
return this.page.$eval(selector, node => node.textContent);
|
||||
}
|
||||
|
||||
wait(extra_ms: number = 0) {
|
||||
return wait(DELAY + extra_ms);
|
||||
}
|
||||
|
||||
capture_requests(fn: () => any): Promise<string[]> {
|
||||
return new Promise((fulfil, reject) => {
|
||||
const requests: string[] = [];
|
||||
const pending: Set<string> = new Set();
|
||||
@@ -120,13 +151,55 @@ export class AppRunner {
|
||||
});
|
||||
}
|
||||
|
||||
end() {
|
||||
return Promise.all([
|
||||
this.browser.close(),
|
||||
new Promise(fulfil => {
|
||||
this.proc.once('exit', fulfil);
|
||||
this.proc.kill();
|
||||
})
|
||||
]);
|
||||
async intercept_requests(interceptor: (request: puppeteer.Request) => void, fn: () => any): Promise<void> {
|
||||
const unique_interceptor = request => interceptor(request);
|
||||
|
||||
this.page.prependListener('request', unique_interceptor);
|
||||
await this.page.setRequestInterception(true);
|
||||
|
||||
const result = await Promise.resolve(fn());
|
||||
|
||||
await this.page.setRequestInterception(false);
|
||||
this.page.removeListener('request', unique_interceptor);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
end() {
|
||||
this.exiting = true;
|
||||
|
||||
this.server.kill();
|
||||
this.browser.close();
|
||||
|
||||
return this.terminate;
|
||||
}
|
||||
}
|
||||
|
||||
interface Deferred<T> extends Promise<T> {
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
settle: (result?: boolean) => void;
|
||||
}
|
||||
|
||||
function settle<T>(this: Deferred<T>, result: boolean) {
|
||||
if (result) {
|
||||
this.resolve();
|
||||
} else {
|
||||
this.reject();
|
||||
}
|
||||
}
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve, reject;
|
||||
|
||||
const deferred = new Promise((_resolve, _reject) => {
|
||||
resolve = _resolve;
|
||||
reject = _reject;
|
||||
}) as Deferred<T>;
|
||||
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
deferred.settle = settle;
|
||||
|
||||
return deferred;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as http from 'http';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
declare let deleted: { id: number };
|
||||
declare let el: any;
|
||||
|
||||
function get(url: string, opts?: any): Promise<{ headers: Record<string, string>, body: string }> {
|
||||
type Response = { headers: http.IncomingHttpHeaders, body: string };
|
||||
|
||||
function get(url: string, opts: http.RequestOptions = {}): Promise<Response> {
|
||||
return new Promise((fulfil, reject) => {
|
||||
const req = http.get(url, opts || {}, res => {
|
||||
const req = http.get(url, opts, res => {
|
||||
res.on('error', reject);
|
||||
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
fulfil({
|
||||
headers: res.headers as Record<string, string>,
|
||||
headers: res.headers,
|
||||
body
|
||||
});
|
||||
});
|
||||
@@ -30,114 +30,104 @@ function get(url: string, opts?: any): Promise<{ headers: Record<string, string>
|
||||
describe('basics', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let prefetch: (href: string) => Promise<void>;
|
||||
let goto: (href: string) => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serves /', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves /?', async () => {
|
||||
await page.goto(`${base}?`);
|
||||
await r.load('/?');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves static route', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await r.load('/a');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'a'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves static route from dir/index.html file', async () => {
|
||||
await page.goto(`${base}/b`);
|
||||
await r.load('/b');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'b'
|
||||
);
|
||||
});
|
||||
|
||||
it('serves dynamic route', async () => {
|
||||
await page.goto(`${base}/test-slug`);
|
||||
await r.load('/test-slug');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'TEST-SLUG'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to a new page without reloading', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
const requests: string[] = await runner.capture(async () => {
|
||||
await page.click('a[href="a"]');
|
||||
const requests: string[] = await r.capture_requests(async () => {
|
||||
await r.page.click('a[href="a"]');
|
||||
await r.wait();
|
||||
});
|
||||
|
||||
assert.deepEqual(requests, []);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'a'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates programmatically', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await start();
|
||||
|
||||
await goto('b');
|
||||
await r.load('/a');
|
||||
await r.sapper.start();
|
||||
await r.sapper.goto('b');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'b'
|
||||
);
|
||||
});
|
||||
|
||||
it('prefetches programmatically', async () => {
|
||||
await page.goto(`${base}/a`);
|
||||
await start();
|
||||
await r.load(`/a`);
|
||||
await r.sapper.start();
|
||||
|
||||
const requests = await runner.capture(() => prefetch('b'));
|
||||
const requests = await r.capture_requests(() => r.sapper.prefetch('b'));
|
||||
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[1], `${base}/b.json`);
|
||||
assert.equal(requests[1], `${r.base}/b.json`);
|
||||
});
|
||||
|
||||
// TODO equivalent test for a webpack app
|
||||
it('sets Content-Type, Link...modulepreload, and Cache-Control headers', async () => {
|
||||
const { headers } = await get(base);
|
||||
const { headers } = await get(r.base);
|
||||
|
||||
assert.equal(
|
||||
headers['content-type'],
|
||||
@@ -157,162 +147,163 @@ describe('basics', function() {
|
||||
});
|
||||
|
||||
it('calls a delete handler', async () => {
|
||||
await page.goto(`${base}/delete-test`);
|
||||
await start();
|
||||
await r.load('/delete-test');
|
||||
await r.sapper.start();
|
||||
|
||||
await page.click('.del');
|
||||
await page.waitForFunction(() => deleted);
|
||||
await r.page.click('.del');
|
||||
await r.page.waitForFunction(() => deleted);
|
||||
|
||||
assert.equal(await page.evaluate(() => deleted.id), 42);
|
||||
assert.equal(await r.page.evaluate(() => deleted.id), 42);
|
||||
});
|
||||
|
||||
it('hydrates initial route', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await page.evaluate(() => {
|
||||
await r.page.evaluate(() => {
|
||||
el = document.querySelector('.hydrate-test');
|
||||
});
|
||||
|
||||
await start();
|
||||
await r.sapper.start();
|
||||
|
||||
assert.ok(await page.evaluate(() => {
|
||||
assert.ok(await r.page.evaluate(() => {
|
||||
return document.querySelector('.hydrate-test') === el;
|
||||
}));
|
||||
});
|
||||
|
||||
it('does not attempt client-side navigation to server routes', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="ambiguous/ok.json"]`);
|
||||
await wait(50);
|
||||
await r.page.click('[href="ambiguous/ok.json"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'ok'
|
||||
);
|
||||
});
|
||||
|
||||
it('allows reserved words as route names', async () => {
|
||||
await page.goto(`${base}/const`);
|
||||
await start();
|
||||
await r.load('/const');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'reserved words are okay as routes'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts value-less query string parameter on server', async () => {
|
||||
await page.goto(`${base}/echo-query?message`);
|
||||
await r.load('/echo-query?message');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"message":""}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts value-less query string parameter on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="echo-query?message"]')
|
||||
await r.page.click('a[href="echo-query?message"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"message":""}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts duplicated query string parameter on server', async () => {
|
||||
await page.goto(`${base}/echo-query?p=one&p=two`);
|
||||
await r.load('/echo-query?p=one&p=two');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"p":["one","two"]}'
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts duplicated query string parameter on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="echo-query?p=one&p=two"]')
|
||||
await r.page.click('a[href="echo-query?p=one&p=two"]')
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'{"p":["one","two"]}'
|
||||
);
|
||||
});
|
||||
|
||||
// skipped because Nightmare doesn't seem to focus the <a> correctly
|
||||
it('resets the active element after navigation', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="a"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="a"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.activeElement.nodeName),
|
||||
await r.page.evaluate(() => document.activeElement.nodeName),
|
||||
'BODY'
|
||||
);
|
||||
});
|
||||
|
||||
it('replaces %sapper.xxx% tags safely', async () => {
|
||||
await page.goto(`${base}/unsafe-replacement`);
|
||||
await start();
|
||||
await r.load('/unsafe-replacement');
|
||||
await r.sapper.start();
|
||||
|
||||
const html = String(await page.evaluate(() => document.body.innerHTML));
|
||||
const html = String(await r.page.evaluate(() => document.body.innerHTML));
|
||||
assert.equal(html.indexOf('%sapper'), -1);
|
||||
});
|
||||
|
||||
it('navigates between routes with empty parts', async () => {
|
||||
await page.goto(`${base}/dirs/foo`);
|
||||
await start();
|
||||
await r.load('/dirs/foo');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'foo');
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
|
||||
await page.click('[href="dirs/bar"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'bar');
|
||||
await r.page.click('[href="dirs/bar"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'bar');
|
||||
});
|
||||
|
||||
it('navigates to ...rest', async () => {
|
||||
await page.goto(`${base}/abc/xyz`);
|
||||
await start();
|
||||
await r.load('/abc/xyz');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'abc,xyz');
|
||||
assert.equal(await r.text('h1'), 'abc,xyz');
|
||||
|
||||
await page.click('[href="xyz/abc/deep"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'xyz,abc');
|
||||
await r.page.click('[href="xyz/abc/deep"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'xyz,abc');
|
||||
|
||||
await page.click(`[href="xyz/abc/qwe/deep.json"]`);
|
||||
await wait(50);
|
||||
await r.page.click('[href="xyz/abc/qwe/deep.json"]');
|
||||
await r.wait();
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'xyz,abc,qwe'
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates between dynamic routes with same segments', async () => {
|
||||
await page.goto(`${base}/dirs/bar/xyz`);
|
||||
await start();
|
||||
await r.load('/dirs/bar/xyz');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'A page');
|
||||
assert.equal(await r.text('h1'), 'A page');
|
||||
|
||||
await page.click('[href="dirs/foo/xyz"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'B page');
|
||||
await r.page.click('[href="dirs/foo/xyz"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'B page');
|
||||
});
|
||||
|
||||
it('runs server route handlers before page handlers, if they match', async () => {
|
||||
const json = await get(`${base}/middleware`, {
|
||||
const json = await get(`${r.base}/middleware`, {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
@@ -320,22 +311,26 @@ describe('basics', function() {
|
||||
|
||||
assert.equal(json.body, '{"json":true}');
|
||||
|
||||
const html = await get(`${base}/middleware`);
|
||||
const html = await get(`${r.base}/middleware`);
|
||||
|
||||
assert.ok(html.body.indexOf('<h1>HTML</h1>') !== -1);
|
||||
});
|
||||
|
||||
it('invalidates page when a segment is skipped', async () => {
|
||||
await page.goto(`${base}/skipped/x/1`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/skipped/x/1');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="skipped/y/1"]');
|
||||
await wait(50);
|
||||
await r.page.click('a[href="skipped/y/1"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'y:1'
|
||||
);
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
42
test/apps/common.js
Normal file
42
test/apps/common.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const { NODE_ENV, PORT } = process.env;
|
||||
|
||||
export const dev = NODE_ENV === 'development';
|
||||
|
||||
export function start(app) {
|
||||
const port = parseInt(PORT) || 0;
|
||||
|
||||
app.listen(port, () => {
|
||||
const address = app.server.address();
|
||||
|
||||
process.env.PORT = address.port;
|
||||
|
||||
send({
|
||||
__sapper__: true,
|
||||
event: 'listening',
|
||||
address
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const properties = ['name', 'message', 'stack', 'code', 'lineNumber', 'fileName'];
|
||||
|
||||
function send(message) {
|
||||
process.send && process.send(message);
|
||||
}
|
||||
|
||||
function send_error(error) {
|
||||
send({
|
||||
__sapper__: true,
|
||||
event: 'error',
|
||||
error: properties.reduce((object, key) => ({...object, [key]: error[key]}), {})
|
||||
})
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
send_error(reason);
|
||||
});
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
send_error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -1,13 +1,14 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use((req, res, next) => {
|
||||
// set test cookie
|
||||
res.setHeader('Set-Cookie', ['a=1; Max-Age=3600', 'b=2; Max-Age=3600']);
|
||||
next();
|
||||
})
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,59 +1,54 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { wait } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('credentials', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('sends cookies when using this.fetch with credentials: "include"', async () => {
|
||||
await page.goto(`${base}/credentials?creds=include`);
|
||||
await r.load('/credentials?creds=include');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'a: 1, b: 2, max-age: undefined'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not send cookies when using this.fetch without credentials', async () => {
|
||||
await page.goto(`${base}/credentials`);
|
||||
await r.load('/credentials');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'unauthorized'
|
||||
);
|
||||
});
|
||||
|
||||
it('delegates to fetch on the client', async () => {
|
||||
await page.goto(base)
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/')
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="credentials?creds=include"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="credentials?creds=include"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'a: 1, b: 2, max-age: undefined'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,78 +1,61 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('css', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let prefetch: (href: string) => Promise<void>;
|
||||
let goto: (href: string) => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, prefetch, goto, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('includes critical CSS with server render', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(255, 0, 0)'
|
||||
);
|
||||
});
|
||||
|
||||
it('loads CSS when navigating client-side', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="foo"]`);
|
||||
await wait(50);
|
||||
await r.page.click(`[href="foo"]`);
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(0, 0, 255)'
|
||||
);
|
||||
});
|
||||
|
||||
it('loads CSS for a lazily-rendered component', async () => {
|
||||
await page.goto(base);
|
||||
await r.load('/');
|
||||
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click(`[href="bar"]`);
|
||||
await wait(50);
|
||||
await r.page.click(`[href="bar"]`);
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => {
|
||||
const h1 = document.querySelector('h1');
|
||||
return getComputedStyle(h1).color;
|
||||
}),
|
||||
await r.page.$eval('h1', node => getComputedStyle(node).color),
|
||||
'rgb(0, 128, 0)'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,68 +1,63 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('encoding', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('encodes routes', async () => {
|
||||
await page.goto(`${base}/fünke`);
|
||||
await r.load('/fünke');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
`I'm afraid I just blue myself`
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params and req.query for server-rendered pages', async () => {
|
||||
await page.goto(`${base}/echo/page/encöded?message=hëllö+wörld&föo=bar&=baz&tel=%2B123456789`);
|
||||
await r.load('/echo/page/encöded?message=hëllö+wörld&föo=bar&=baz&tel=%2B123456789');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded {"message":"hëllö wörld","föo":"bar","":"baz","tel":"+123456789"}'
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params and req.query for client-rendered pages', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a');
|
||||
await wait(50);
|
||||
await r.page.click('a');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded {"message":"hëllö wörld","föo":"bar","":"baz","tel":"+123456789"}'
|
||||
);
|
||||
});
|
||||
|
||||
it('encodes req.params for server routes', async () => {
|
||||
await page.goto(`${base}/echo/server-route/encöded`);
|
||||
await r.load('/echo/server-route/encöded');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'encöded'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
@@ -7,142 +6,137 @@ import { wait } from '../../utils';
|
||||
describe('errors', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('handles missing route on server', async () => {
|
||||
await page.goto(`${base}/nope`);
|
||||
await r.load('/nope');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles missing route on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
|
||||
await page.click('[href="nope"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="nope"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles explicit 4xx on server', async () => {
|
||||
await page.goto(`${base}/blog/nope`);
|
||||
await r.load('/blog/nope');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles explicit 4xx on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="blog/nope"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="blog/nope"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'404'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles error on server', async () => {
|
||||
await page.goto(`${base}/throw`);
|
||||
await r.load('/throw');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'500'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles error on client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="throw"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="throw"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'500'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not serve error page for explicit non-page errors', async () => {
|
||||
await page.goto(`${base}/nope.json`);
|
||||
await r.load('/nope.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'nope'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not serve error page for thrown non-page errors', async () => {
|
||||
await page.goto(`${base}/throw.json`);
|
||||
await r.load('/throw.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'oops'
|
||||
);
|
||||
});
|
||||
|
||||
it('execute error page hooks', async () => {
|
||||
await page.goto(`${base}/some-throw-page`);
|
||||
await start();
|
||||
await wait(50);
|
||||
await r.load('/some-throw-page');
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h2', node => node.textContent),
|
||||
await r.text('h2'),
|
||||
'success'
|
||||
);
|
||||
})
|
||||
|
||||
it('does not serve error page for async non-page error', async () => {
|
||||
await page.goto(`${base}/async-throw.json`);
|
||||
await r.load('/async-throw.json');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.body.textContent),
|
||||
await r.text('body'),
|
||||
'oops'
|
||||
);
|
||||
});
|
||||
|
||||
it('clears props.error on successful render', async () => {
|
||||
await page.goto(`${base}/no-error`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/no-error');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="enhance-your-calm"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), '420');
|
||||
await r.page.click('[href="enhance-your-calm"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), '420');
|
||||
|
||||
await page.goBack();
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'No error here');
|
||||
await r.page.goBack();
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'No error here');
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,14 +2,12 @@ import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
import { start, dev } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use(
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -6,14 +6,12 @@ describe('export-webpack', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname, bundler: 'webpack' });
|
||||
await api.export({ cwd: __dirname, bundler: 'webpack' });
|
||||
});
|
||||
before('build app', () => api.build({ cwd: __dirname, bundler: 'webpack' }));
|
||||
before('export app', () => api.export({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('injects <link rel=preload> tags', () => {
|
||||
const index = fs.readFileSync(`${__dirname}/__sapper__/export/index.html`, 'utf8');
|
||||
assert.ok(/rel=preload/.test(index));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2,14 +2,12 @@ import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
import { start, dev } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use(
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -6,11 +6,10 @@ describe('export', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
await api.export({ cwd: __dirname });
|
||||
});
|
||||
before('build app', () => api.build({ cwd: __dirname }));
|
||||
before('export app', () => api.export({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('crawls a site', () => {
|
||||
const files = walk(`${__dirname}/__sapper__/export`);
|
||||
|
||||
@@ -45,4 +44,4 @@ describe('export', function() {
|
||||
});
|
||||
|
||||
// TODO test timeout, basepath
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
const app = polka().use(sapper.middleware({
|
||||
ignore: [
|
||||
@@ -16,4 +16,4 @@ const app = polka().use(sapper.middleware({
|
||||
app.get('/'+uri, (req, res) => res.end(uri));
|
||||
});
|
||||
|
||||
app.listen(PORT);
|
||||
start(app);
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('ignore', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('respects `options.ignore` values (RegExp)', async () => {
|
||||
await page.goto(`${base}/foobar`);
|
||||
await r.load('/foobar');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'foobar'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (String #1)', async () => {
|
||||
await page.goto(`${base}/buzz`);
|
||||
await r.load('/buzz');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'buzz'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (String #2)', async () => {
|
||||
await page.goto(`${base}/fizzer`);
|
||||
await r.load('/fizzer');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'fizzer'
|
||||
);
|
||||
});
|
||||
|
||||
it('respects `options.ignore` values (Function)', async () => {
|
||||
await page.goto(`${base}/hello`);
|
||||
await r.load('/hello');
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => document.documentElement.textContent),
|
||||
await r.text('body'),
|
||||
'hello'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,33 +1,25 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('layout', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('only recreates components when necessary', async () => {
|
||||
await page.goto(`${base}/foo/bar/baz`);
|
||||
await r.load('/foo/bar/baz');
|
||||
|
||||
const text1 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
const text1 = await r.text('#sapper');
|
||||
assert.deepEqual(text1.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: baz 1',
|
||||
@@ -35,8 +27,8 @@ describe('layout', function() {
|
||||
'child segment: baz'
|
||||
]);
|
||||
|
||||
await start();
|
||||
const text2 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
await r.sapper.start();
|
||||
const text2 = await r.text('#sapper');
|
||||
assert.deepEqual(text2.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: baz 1',
|
||||
@@ -44,10 +36,10 @@ describe('layout', function() {
|
||||
'child segment: baz'
|
||||
]);
|
||||
|
||||
await page.click('[href="foo/bar/qux"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="foo/bar/qux"]');
|
||||
await r.wait();
|
||||
|
||||
const text3 = String(await page.evaluate(() => document.querySelector('#sapper').textContent));
|
||||
const text3 = await r.text('#sapper');
|
||||
assert.deepEqual(text3.split('\n').map(str => str.trim()).filter(Boolean), [
|
||||
'y: bar 1',
|
||||
'z: qux 2',
|
||||
@@ -55,4 +47,8 @@ describe('layout', function() {
|
||||
'child segment: qux'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { wait } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
declare const fulfil: () => Promise<void>;
|
||||
@@ -9,112 +7,106 @@ declare const fulfil: () => Promise<void>;
|
||||
describe('preloading', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serializes Set objects returned from preload', async () => {
|
||||
await page.goto(`${base}/preload-values/set`);
|
||||
await r.load('/preload-values/set');
|
||||
|
||||
assert.equal(await title(), 'true');
|
||||
assert.equal(await r.text('h1'), 'true');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'true');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'true');
|
||||
});
|
||||
|
||||
it('prevent crash if preload return nothing', async () => {
|
||||
await page.goto(`${base}/preload-nothing`);
|
||||
await r.load('/preload-nothing');
|
||||
|
||||
await start();
|
||||
await wait(50);
|
||||
await r.sapper.start();
|
||||
|
||||
assert.equal(await title(), 'Page loaded');
|
||||
assert.equal(await r.text('h1'), 'Page loaded');
|
||||
});
|
||||
|
||||
it('bails on custom classes returned from preload', async () => {
|
||||
await page.goto(`${base}/preload-values/custom-class`);
|
||||
await r.load('/preload-values/custom-class');
|
||||
|
||||
assert.equal(await title(), '42');
|
||||
assert.equal(await r.text('h1'), '42');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), '42');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), '42');
|
||||
});
|
||||
|
||||
it('sets preloading true when appropriate', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="slow-preload"]');
|
||||
await r.page.click('a[href="slow-preload"]');
|
||||
|
||||
assert.ok(await page.evaluate(() => !!document.querySelector('progress')));
|
||||
assert.ok(await r.page.evaluate(() => !!document.querySelector('progress')));
|
||||
|
||||
await page.evaluate(() => fulfil());
|
||||
assert.ok(await page.evaluate(() => !document.querySelector('progress')));
|
||||
await r.page.evaluate(() => fulfil());
|
||||
assert.ok(await r.page.evaluate(() => !document.querySelector('progress')));
|
||||
});
|
||||
|
||||
it('runs preload in root component', async () => {
|
||||
await page.goto(`${base}/preload-root`);
|
||||
assert.equal(await title(), 'root preload function ran: true');
|
||||
await r.load('/preload-root');
|
||||
assert.equal(await r.text('h1'), 'root preload function ran: true');
|
||||
});
|
||||
|
||||
it('cancels navigation if subsequent navigation occurs during preload', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('a[href="slow-preload"]');
|
||||
await wait(100);
|
||||
await page.click('a[href="foo"]');
|
||||
await r.page.click('a[href="slow-preload"]');
|
||||
await r.wait();
|
||||
await r.page.click('a[href="foo"]');
|
||||
|
||||
assert.equal(page.url(), `${base}/foo`);
|
||||
assert.equal(await title(), 'foo');
|
||||
assert.equal(r.page.url(), `${r.base}/foo`);
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
|
||||
await page.evaluate(() => fulfil());
|
||||
await wait(100);
|
||||
assert.equal(page.url(), `${base}/foo`);
|
||||
assert.equal(await title(), 'foo');
|
||||
await r.page.evaluate(() => fulfil());
|
||||
await r.wait();
|
||||
assert.equal(r.page.url(), `${r.base}/foo`);
|
||||
assert.equal(await r.text('h1'), 'foo');
|
||||
});
|
||||
|
||||
it('navigates to prefetched urls', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.hover('a[href="prefetch/qwe"]');
|
||||
await wait(100);
|
||||
await page.hover('a[href="prefetch/xyz"]');
|
||||
await wait(100);
|
||||
await r.page.hover('a[href="prefetch/qwe"]');
|
||||
await r.wait(50);
|
||||
await r.page.hover('a[href="prefetch/xyz"]');
|
||||
await r.wait(50);
|
||||
|
||||
await page.click('a[href="prefetch/qwe"]');
|
||||
await wait(50);
|
||||
await r.page.click('a[href="prefetch/qwe"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'qwe'
|
||||
);
|
||||
|
||||
await page.goto(`${base}/prefetch`);
|
||||
await wait(50);
|
||||
await r.load('/prefetch');
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'prefetch'
|
||||
);
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -2,138 +2,137 @@ import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('redirects', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start({
|
||||
requestInterceptor: (interceptedRequest) => {
|
||||
if (/example\.com/.test(interceptedRequest.url())) {
|
||||
interceptedRequest.respond({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: `<h1>external</h1>`
|
||||
});
|
||||
} else {
|
||||
interceptedRequest.continue();
|
||||
}
|
||||
}
|
||||
}));
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('redirects on server', async () => {
|
||||
await page.goto(`${base}/redirect-from`);
|
||||
await r.load('/redirect-from');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-from"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-from"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to root on server', async () => {
|
||||
await page.goto(`${base}/redirect-to-root`);
|
||||
await r.load('/redirect-to-root');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/`
|
||||
r.page.url(),
|
||||
`${r.base}/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'root'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to root in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-to-root"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-to-root"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/`
|
||||
r.page.url(),
|
||||
`${r.base}/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'root'
|
||||
);
|
||||
});
|
||||
|
||||
const interceptor = (request: puppeteer.Request) => {
|
||||
if (/example\.com/.test(request.url())) {
|
||||
request.respond({
|
||||
status: 200,
|
||||
contentType: 'text/html',
|
||||
body: `<h1>external</h1>`
|
||||
});
|
||||
} else {
|
||||
request.continue();
|
||||
}
|
||||
};
|
||||
|
||||
it('redirects to external URL on server', async () => {
|
||||
await page.goto(`${base}/redirect-to-external`);
|
||||
await r.intercept_requests(interceptor, async () => {
|
||||
await r.load('/redirect-to-external');
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
r.page.url(),
|
||||
`https://example.com/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'external'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects to external URL in client', async () => {
|
||||
await page.goto(base);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-to-external"]');
|
||||
await wait(50);
|
||||
await r.intercept_requests(interceptor, async () => {
|
||||
await r.page.click('[href="redirect-to-external"]');
|
||||
await r.wait();
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
r.page.url(),
|
||||
`https://example.com/`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'external'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,93 +1,87 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
describe('scroll', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('scrolls to active deeplink', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
});
|
||||
|
||||
it('scrolls to any deeplink if it was already active', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
|
||||
let scrollY = await page.evaluate(() => window.scrollY);
|
||||
let scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
|
||||
scrollY = await page.evaluate(() => {
|
||||
scrollY = await r.page.evaluate(() => {
|
||||
window.scrollTo(0, 0)
|
||||
return window.scrollY
|
||||
});
|
||||
assert.ok(scrollY === 0, String(scrollY));
|
||||
|
||||
await page.click('[href="tall-page#foo"]');
|
||||
scrollY = await page.evaluate(() => window.scrollY);
|
||||
await r.page.click('[href="tall-page#foo"]');
|
||||
scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0, String(scrollY));
|
||||
});
|
||||
|
||||
it('resets scroll when a link is clicked', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="another-tall-page"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
await page.evaluate(() => window.scrollY),
|
||||
await r.page.evaluate(() => window.scrollY),
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves scroll when a link with sapper-noscroll is clicked', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page"][sapper-noscroll]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="another-tall-page"][sapper-noscroll]');
|
||||
await r.wait();
|
||||
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
|
||||
assert.ok(scrollY > 0);
|
||||
});
|
||||
|
||||
it('scrolls into a deeplink on a new page', async () => {
|
||||
await page.goto(`${base}/tall-page#foo`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/tall-page#foo');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="another-tall-page#bar"]');
|
||||
await wait(50);
|
||||
assert.equal(await title(), 'Another tall page');
|
||||
const scrollY = await page.evaluate(() => window.scrollY);
|
||||
await r.page.click('[href="another-tall-page#bar"]');
|
||||
await r.wait();
|
||||
assert.equal(await r.text('h1'), 'Another tall page');
|
||||
const scrollY = await r.page.evaluate(() => window.scrollY);
|
||||
assert.ok(scrollY > 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use((req, res, next) => {
|
||||
req.hello = 'hello';
|
||||
res.locals = { name: 'world' };
|
||||
@@ -15,5 +15,6 @@ polka()
|
||||
title: `${req.hello} ${res.locals.name}`
|
||||
})
|
||||
})
|
||||
)
|
||||
.listen(PORT);
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import { build } from '../../../api';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
|
||||
describe('session', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, page, start, title } = await runner.start());
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('renders session props', async () => {
|
||||
await page.goto(`${base}/session`);
|
||||
await r.load('/session');
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await page.click('button');
|
||||
assert.equal(await title(), 'changed');
|
||||
await r.page.click('button');
|
||||
assert.equal(await r.text('h1'), 'changed');
|
||||
});
|
||||
|
||||
it('preloads session props', async () => {
|
||||
await page.goto(`${base}/preloaded`);
|
||||
await r.load('/preloaded');
|
||||
|
||||
assert.equal(await title(), 'hello world');
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await start();
|
||||
assert.equal(await title(), 'hello world');
|
||||
await r.sapper.start();
|
||||
assert.equal(await r.text('h1'), 'hello world');
|
||||
|
||||
await page.click('button');
|
||||
assert.equal(await title(), 'changed');
|
||||
await r.page.click('button');
|
||||
assert.equal(await r.text('h1'), 'changed');
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,15 +2,14 @@ import sirv from 'sirv';
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT, NODE_ENV } = process.env;
|
||||
const dev = NODE_ENV === 'development';
|
||||
import { start, dev } from '../../common.js';
|
||||
|
||||
polka()
|
||||
const app = polka()
|
||||
.use(
|
||||
'custom-basepath',
|
||||
sirv('static', { dev }),
|
||||
sapper.middleware()
|
||||
)
|
||||
.listen(PORT, err => {
|
||||
if (err) console.log('error', err);
|
||||
});
|
||||
);
|
||||
|
||||
start(app);
|
||||
|
||||
|
||||
@@ -1,51 +1,36 @@
|
||||
import * as assert from 'assert';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
import * as api from '../../../api';
|
||||
import { walk } from '../../utils';
|
||||
import { AppRunner } from '../AppRunner';
|
||||
import { wait } from '../../utils';
|
||||
|
||||
|
||||
describe('with-basepath', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
let runner: AppRunner;
|
||||
let page: puppeteer.Page;
|
||||
let base: string;
|
||||
|
||||
// helpers
|
||||
let start: () => Promise<void>;
|
||||
let prefetchRoutes: () => Promise<void>;
|
||||
let title: () => Promise<string>;
|
||||
let r: AppRunner;
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await api.build({ cwd: __dirname });
|
||||
|
||||
await api.export({
|
||||
cwd: __dirname,
|
||||
basepath: '/custom-basepath'
|
||||
});
|
||||
|
||||
runner = new AppRunner(__dirname, '__sapper__/build/server/server.js');
|
||||
({ base, start, page, prefetchRoutes, title } = await runner.start());
|
||||
before('build app', () => api.build({ cwd: __dirname }));
|
||||
before('export app', () => api.export({ cwd: __dirname, basepath: '/custom-basepath' }));
|
||||
before('start runner', async () => {
|
||||
r = await new AppRunner().start(__dirname);
|
||||
});
|
||||
|
||||
after(() => runner.end());
|
||||
after(() => r && r.end());
|
||||
|
||||
// tests
|
||||
it('serves /custom-basepath', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await r.load('/custom-basepath');
|
||||
|
||||
assert.equal(
|
||||
await page.$eval('h1', node => node.textContent),
|
||||
await r.text('h1'),
|
||||
'Great success!'
|
||||
);
|
||||
});
|
||||
|
||||
it('emits a basepath message', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await r.load('/custom-basepath');
|
||||
|
||||
assert.deepEqual(runner.messages, [{
|
||||
assert.deepEqual(r.messages, [{
|
||||
__sapper__: true,
|
||||
event: 'basepath',
|
||||
basepath: '/custom-basepath'
|
||||
@@ -71,35 +56,39 @@ describe('with-basepath', function() {
|
||||
});
|
||||
|
||||
it('redirects on server', async () => {
|
||||
await page.goto(`${base}/custom-basepath/redirect-from`);
|
||||
await r.load('/custom-basepath/redirect-from');
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/custom-basepath/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/custom-basepath/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
|
||||
it('redirects in client', async () => {
|
||||
await page.goto(`${base}/custom-basepath`);
|
||||
await start();
|
||||
await prefetchRoutes();
|
||||
await r.load('/custom-basepath');
|
||||
await r.sapper.start();
|
||||
await r.sapper.prefetchRoutes();
|
||||
|
||||
await page.click('[href="redirect-from"]');
|
||||
await wait(50);
|
||||
await r.page.click('[href="redirect-from"]');
|
||||
await r.wait();
|
||||
|
||||
assert.equal(
|
||||
page.url(),
|
||||
`${base}/custom-basepath/redirect-to`
|
||||
r.page.url(),
|
||||
`${r.base}/custom-basepath/redirect-to`
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
await title(),
|
||||
await r.text('h1'),
|
||||
'redirected'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('survives the tests with no server errors', () => {
|
||||
assert.deepEqual(r.errors, []);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -7,10 +7,9 @@ describe('with-sourcemaps-webpack', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname, bundler: 'webpack' });
|
||||
});
|
||||
before('build app', () => build({ cwd: __dirname, bundler: 'webpack' }));
|
||||
|
||||
// tests
|
||||
it('does not put sourcemap files in service worker shell', async () => {
|
||||
const service_worker_source = fs.readFileSync(`${__dirname}/src/node_modules/@sapper/service-worker.js`, 'utf-8');
|
||||
const shell_source = /shell = (\[[\s\S]+?\])/.exec(service_worker_source)[1];
|
||||
@@ -23,4 +22,4 @@ describe('with-sourcemaps-webpack', function() {
|
||||
const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import polka from 'polka';
|
||||
import * as sapper from '@sapper/server';
|
||||
|
||||
const { PORT } = process.env;
|
||||
import { start } from '../../common.js';
|
||||
|
||||
polka()
|
||||
.use(sapper.middleware())
|
||||
.listen(PORT);
|
||||
const app = polka()
|
||||
.use(sapper.middleware());
|
||||
|
||||
start(app);
|
||||
|
||||
@@ -7,10 +7,9 @@ describe('with-sourcemaps', function() {
|
||||
this.timeout(10000);
|
||||
|
||||
// hooks
|
||||
before(async () => {
|
||||
await build({ cwd: __dirname });
|
||||
});
|
||||
before('build app', () => build({ cwd: __dirname }));
|
||||
|
||||
// tests
|
||||
it('does not put sourcemap files in service worker shell', async () => {
|
||||
const service_worker_source = fs.readFileSync(`${__dirname}/src/node_modules/@sapper/service-worker.js`, 'utf-8');
|
||||
const shell_source = /shell = (\[[\s\S]+?\])/.exec(service_worker_source)[1];
|
||||
@@ -23,4 +22,4 @@ describe('with-sourcemaps', function() {
|
||||
const sourcemapFiles = fs.readdirSync(clientShellDir).filter(_ => _.endsWith('.map'));
|
||||
assert.ok(sourcemapFiles.length > 0, 'sourcemap files exist');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user