Skip to content

Implement JS/no-JS tests #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/kit/src/api/build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export async function build(config) {
static_dir: paths.static,
template,
manifest,
target: ${s(config.target)},
target: ${s(config.target)},${config.startGlobal ? `\n\t\t\t\t\tstart_global: ${s(config.startGlobal)},` : ''}
client,
root,
setup,
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/api/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ class Watcher extends EventEmitter {
root,
setup,
load: (route) => load(route.url.replace(/\.\w+$/, '.js')), // TODO is the replace still necessary?
only_prerender: false
only_prerender: false,
start_global: this.config.startGlobal
}
);

Expand Down
1 change: 1 addition & 0 deletions packages/kit/src/api/load_config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import relative from 'require-relative';

const default_config = {
target: null,
startGlobal: null, // used for testing
paths: {
static: 'static',
routes: 'src/routes',
Expand Down
4 changes: 2 additions & 2 deletions packages/kit/src/renderer/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,12 @@ async function get_response({
.map((dep) => `<link rel="stylesheet" href="/_app/${dep}">`)
.join('\n\t\t\t')}
${options.dev ? `<style>${rendered.css.code}</style>` : ''}
`.replace(/^\t{2}/gm, ''); // TODO add links
`.replace(/^\t{2}/gm, '');

const body = `${rendered.html}
<script type="module">
import { start } from '/_app/${options.client.entry}';
start({
${options.start_global ? `window.${options.start_global} = () => ` : ''}start({
target: ${options.target ? `document.querySelector(${JSON.stringify(options.target)})` : 'document.body'},
base: "${base}",
status: ${status},
Expand Down
13 changes: 11 additions & 2 deletions packages/kit/src/runtime/internal/renderer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,16 @@ export class Renderer {
const props = {
stores: this.stores,
error: this.initial.error,
status: this.initial.status
status: this.initial.status,
page: {
...page.page,
params: {}
}
};

if (!this.initial.error) {
if (this.initial.error) {
props.components = [this.layout.default];
} else {
const hydrated = await this.hydrate(page);

if (hydrated.redirect) {
Expand Down Expand Up @@ -221,6 +227,9 @@ export class Renderer {
: {}
);

// TODO weird to have side-effects inside a map, but
// if they're not here, then setting props_n objects
// only for changed parts becomes trickier
props.components[i] = component;
props[`props_${i}`] = preloaded;

Expand Down
10 changes: 6 additions & 4 deletions test/apps/basics/src/routes/delete-route/__tests__.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export default function (test) {
test('calls a delete handler', async ({ visit, wait_for_text, click }) => {
await visit('/delete-route');
test('calls a delete handler', async ({ visit, wait_for_text, click, js }) => {
if (js) {
await visit('/delete-route');

await click('.del');
await wait_for_text('h1', 'deleted 42');
await click('.del');
await wait_for_text('h1', 'deleted 42');
}
});
}
42 changes: 26 additions & 16 deletions test/apps/basics/src/routes/errors/__tests__.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ import * as assert from 'uvu/assert';

export default function(test, is_dev) {
if (is_dev) {
test('client-side errors', async ({ visit, contains }) => {
await visit('/errors/clientside');

// this is the Snowpack error overlay (TODO dev mode only)
assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
test('client-side errors', async ({ visit, contains, js }) => {
if (js) {
try {
await visit('/errors/clientside');
} catch (error) {
assert.ok(/Crashing now/.test(error.message));
} finally {
// this is the Snowpack error overlay
assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
}
}
});
}

Expand All @@ -19,12 +25,14 @@ export default function(test, is_dev) {
assert.ok(await contains('custom error page'));
});

test('client-side preload errors', async ({ visit, contains }) => {
await visit('/errors/preload-client');
test('client-side preload errors', async ({ visit, contains, js }) => {
if (js) {
await visit('/errors/preload-client');

assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
assert.ok(await contains('custom error page'));
assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
assert.ok(await contains('custom error page'));
}
});

test('server-side preload errors', async ({ visit, contains }) => {
Expand All @@ -35,12 +43,14 @@ export default function(test, is_dev) {
assert.ok(await contains('custom error page'));
});

test('client-side module context errors', async ({ visit, contains }) => {
await visit('/errors/module-scope-client');
test('client-side module context errors', async ({ visit, contains, js }) => {
if (js) {
await visit('/errors/module-scope-client');

assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
assert.ok(await contains('custom error page'));
assert.ok(await contains('Custom layout'));
assert.ok(await contains('Crashing now'));
assert.ok(await contains('custom error page'));
}
});

test('server-side module context errors', async ({ visit, contains }) => {
Expand Down
49 changes: 28 additions & 21 deletions test/apps/basics/src/routes/routing/__tests__.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,41 +48,48 @@ export default function (test) {
prefetch_routes,
capture_requests,
click,
wait_for_function
wait_for_function,
js
}) => {
await visit('/routing/');
if (js) {
await visit('/routing/');

await prefetch_routes().catch(e => {
// from error handler tests; ignore
if (!e.message.includes('Crashing now')) throw e;
});
await prefetch_routes().catch(e => {
// from error handler tests; ignore
if (!e.message.includes('Crashing now')) throw e;
});

const requests = await capture_requests(async () => {
await click('a[href="a"]');
const requests = await capture_requests(async () => {
await click('a[href="a"]');

await wait_for_function(() => document.location.pathname == '/routing/a');
await wait_for_function(() => document.location.pathname == '/routing/a');

assert.equal(await text('h1'), 'a');
});
assert.equal(await text('h1'), 'a');
});

assert.equal(requests, []);
assert.equal(requests, []);
}
});

test('navigates programmatically', async ({ visit, text, goto }) => {
await visit('/routing/a');
test('navigates programmatically', async ({ visit, text, goto, js }) => {
if (js) {
await visit('/routing/a');

await goto('/routing/b');
await goto('/routing/b');

assert.equal(await text('h1'), 'b');
assert.equal(await text('h1'), 'b');
}
});

test('prefetches programmatically', async ({ visit, base, capture_requests, prefetch }) => {
await visit('/routing/a');
test('prefetches programmatically', async ({ visit, base, capture_requests, prefetch, js }) => {
if (js) {
await visit('/routing/a');

const requests = await capture_requests(() => prefetch('b'));
const requests = await capture_requests(() => prefetch('b'));

assert.equal(requests.length, 2);
assert.equal(requests[1], `${base}/routing/b.json`);
assert.equal(requests.length, 2);
assert.equal(requests[1], `${base}/routing/b.json`);
}
});

test('does not attempt client-side navigation to server routes', async ({
Expand Down
8 changes: 4 additions & 4 deletions test/apps/basics/src/routes/store/__tests__.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as assert from 'uvu/assert';

export default function (test) {
test('page store functions as expected', async ({ visit, page, click, text, wait_for_text }) => {
test('page store functions as expected', async ({ visit, evaluate, click, text, wait_for_text, js }) => {
await visit('/store/');

assert.equal(await text('h1'), 'Test');
assert.equal(await text('h2'), 'Called 1 time');
assert.equal(await text('h2'), 'Calls: 1');

await click('a[href="result"]');

await wait_for_text('h1', 'Result');
await wait_for_text('h2', 'Called 1 time');
await wait_for_text('h2', js ? 'Calls: 1' : 'Calls: 0');

const oops = await page.evaluate(() => window.oops);
const oops = await evaluate(() => window.oops);
assert.ok(!oops, oops);
});
}
14 changes: 5 additions & 9 deletions test/apps/basics/src/routes/store/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,22 @@

const { page, session } = getStores();

let call_count = 0;
let calls = 0;

onMount(() => {
session.set(call_count);
session.set(calls);
});

const unsubscribe = page.subscribe($page => {
call_count++;
session.set(call_count);
calls++;
session.set(calls);
});

onDestroy(unsubscribe);

const throw_error = () => {
throw new Error('This should not happen');
}
</script>

<h1>Test</h1>
<h2>Called {call_count} time</h2>
<h2>Calls: {calls}</h2>
<a href="result">results</a>

{#if $page.path === '/store/result'}
Expand Down
8 changes: 4 additions & 4 deletions test/apps/basics/src/routes/store/result.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import { get } from 'svelte/store';

const { session } = getStores();
let call_count = 0;

let calls = 0;

onMount(() => {
call_count = get(session);
calls = get(session);
});
</script>

<h1>Result</h1>
<h2>Called {call_count} time</h2>
<h2>Calls: {calls}</h2>
11 changes: 7 additions & 4 deletions test/apps/basics/svelte.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module.exports = {
// By default, `npm run build` will create a standard Node app.
// You can create optimized builds for different platforms by
// specifying a different adapter
adapter: '@sveltejs/adapter-node'
// TODO adapterless builds
adapter: '@sveltejs/adapter-node',

// this creates `window.start` which starts the app, instead of
// it starting automatically — allows test runner to control
// when hydration occurs
startGlobal: 'start'
};
3 changes: 3 additions & 0 deletions test/apps/options/snowpack.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Consult https://www.snowpack.dev to learn about these options
module.exports = {
extends: '@sveltejs/snowpack-config',
devOptions: {
output: 'stream'
},
mount: {
'source/components': '/components'
},
Expand Down
8 changes: 7 additions & 1 deletion test/apps/options/svelte.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
// TODO adapterless builds
adapter: '@sveltejs/adapter-node',

paths: {
Expand All @@ -7,5 +8,10 @@ module.exports = {
template: 'source/template.html'
},

target: '#content-goes-here'
target: '#content-goes-here',

// this creates `window.start` which starts the app, instead of
// it starting automatically — allows test runner to control
// when hydration occurs
startGlobal: 'start'
};
4 changes: 2 additions & 2 deletions test/apps/options/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import * as assert from 'uvu/assert';
import { runner } from '../../../runner'; // TODO make this a package?

runner((test, is_dev) => {
test('serves /', async ({ visit, contains }) => {
test('serves /', async ({ visit, contains, js }) => {
await visit('/');
assert.ok(await contains('I am in the template'), 'Should show custom template contents');
assert.ok(await contains('We\'re on index.svelte'), 'Should show page contents');
assert.ok(await contains('Hello from the client!'), 'Should run JavaScript');
assert.ok(await contains(`Hello from the ${js ? 'client' : 'server'}!`), 'Should run JavaScript');
});
});
30 changes: 28 additions & 2 deletions test/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ async function setup({ port }) {

return {
base,
page,
visit: path => page.goto(base + path),
contains: async str => (await page.innerHTML('body')).includes(str),
html: async selector => await page.innerHTML(selector, { timeout: defaultTimeout }),
Expand Down Expand Up @@ -71,7 +70,34 @@ export function runner(callback) {
suite.before(before);
suite.after(after);

callback(suite, is_dev);
const duplicate = (test_fn) => {
return (name, callback) => {
test_fn(`${name} [no js]`, async context => {
await callback({
...context,
js: false
});
});

test_fn(`${name} [js]`, async context => {
await callback({
...context,
js: true,
visit: async (path) => {
const res = await context.visit(path);
await context.evaluate(() => window.start());
return res;
}
});
});
}
}

const test = duplicate(suite);
test.skip = duplicate(suite.skip);
test.only = duplicate(suite.only);

callback(test, is_dev);

suite.run();
}
Expand Down