-
-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Template tests #19
Comments
The template lives here: https://github.com/sveltejs/kit/tree/master/packages/create-svelte/template |
A few questions we'll need to answer:
|
I won't touch jest with a barge pole, personally, and I don't see any advantage to mocha over uvu. uvu is lean enough that we could get away with including it by default. Playwright is a different matter though. I'm inclined to think it should be an optional add-on for that reason. (At any rate, Playwright by itself probably isn't sufficient for integration testing anyway, it's too low-level.) But it's hard to know what testing setup to suggest when collectively we haven't really figured out what sort of things should live in a test suite |
If we can figure a relatively bullet-proof way to get uvu + JSDOM to work without too much boilerplate we should for it. It isn't perfect but the trade-offs are reasonable for most projects. Luke would be the best person to comment here although I'll be trying to make that work soon myself. |
Here's an example of Svelte + uvu + JSDOM: https://github.com/lukeed/uvu/tree/master/examples/svelte There's also Svelte Testing Library: https://testing-library.com/docs/svelte-testing-library/example/ |
The jsdom + svelte example works well. I have clients running that today in production. There's also a PR in uvu repo about adding preprocessor support to the example. It's on my todo list to fix that up a bit, but it'll be another good example to point at. For the record, uvu browser testing story will be its own package, which auto invokes playwright for you. I'm playing with it still to make it as lean as possible, and set it up in a way such that you can auto-rerun tests when related source files change. That may be a bit too lofty of a goal, but was something I was shooting for regardless. Might also add a Also, not certain, but I've heard people adding svelte-testing-library to the example and having it just work. Haven't tried myself since the helpers included in example effectively supplant STL entirely. |
The main value of testing library is the neat semantic helpers, it has a few benefits. But being able to do However, it would be easy for users to modify the |
I spent a few hours trying the various testing approaches.
The uvu pull request that adds preprocessor support borrows the same code. I think this is because the transformation APIs they rely on are synchronous, whereas the preprocessor is async, and they use The uvu solution also relies on I also tried the approach of https://dev.to/d_ir/introduction-4cep that uses Rollup to build the tests. He seems to have a lot of problems with ES6 vs CJS modules and indeed his repository no longer seems to work. I get an error related to ES6 modules that I could not diagnose. |
Possible deasync alternative: https://github.com/sindresorhus/make-synchronous |
That looks similar to an approach I’ve taken before, probably similar to what like is going to dig up: https://github.com/pngwn/svelte-test/blob/master/transform.js |
For jest, it looks like there's an issue jestjs/jest#9504 and PR jestjs/jest#9889 to support async transformers. I wonder if that's something that could be added to |
|
(I get some error about it trying to serialize the function and failing) Anyway, all these async-to-sync solutions are kludges; I think @benmccann 's suggestion of looking into making preprocessing synchronous seems like the best way forward.
uvu uses the Node API |
Making the preprocess synchronous will not work for all preprocessors I think. For the 90% case of typescript it will though. But the interface of preprocess is marked as being asynchronous, but then we require people to make it synchronous again, which feels wrong. I don't know, maybe the |
I tried replacing this with a synchronous |
It needs to be done dynamically (of sync or async is another question) because theoretically it can handle a lot of preprocessors, but if you statically require/import them, you would need to install all of them even if you only use one. |
Why does the testing library need to invoke the preprocessor? Can we just compile the code before running the tests? |
I thought about this, too in the sense of |
That would certainly sidestep all these issues with preprocessing. https://dev.to/d_ir/setting-up-a-svelte-test-environment-2f3e sets up a new Rollup build just for the tests that he configures to have multiple entry points. He seems to have had some challenges with it (see under "Rollup configuration") and it all goes a bit over my head. I guess it might be a bit confusing to have two different builds. |
Could snowpack do the heavy lifting for us here? If we started the 'compile all files in watch mode' process but without the dev server parts? Then we just need to map the test imports back to those transpiled modules. We already have everything setup in terms of preprocessors etc. We just need to consider any node/ browser inconsistencies. |
I found my Jest + Svelte runner. It's similar to what @pngwn linked but for whatever reason I remember it being more consistent? Not sure why – haven't used it in a year or so // package.json
{
"transform": {
"^.+\\.svelte$": "<rootDir>/.jest/svelte.js"
},
}
// .jest/svelte.js
const { join } = require('path');
const { execFileSync } = require('child_process');
const runner = join(__dirname, 'runner.js');
// jest doesn't support async transforms
exports.process = (code, file) => {
const output = execFileSync('node', [runner, file]);
return JSON.parse(output); //=> { code, map }
}
// .jest/runner.js
const { resolve } = require('path');
const { promisify } = require('util');
const svelte = require('svelte/compiler');
const { readFile, existsSync } = require('fs');
const config = require('../svelte.config');
const read = promisify(readFile);
const [input] = process.argv.slice(2);
if (!input) {
console.error('Did not receive a filename!')
process.exit(1);
}
const filename = resolve('.', input);
if (!existsSync(filename)) {
console.error(`File does not exist!\n>${filename}`);
process.exit(1);
}
// Begin
async function compile() {
const text = await read(input, 'utf8');
const plain = await svelte.preprocess(text, config.preprocess, { filename });
const output = svelte.compile(plain.code, {
filename,
format: 'cjs',
// ...config.compilerOptions
accessors: true,
css: false,
dev: true,
});
const { code, map } = output.js;
// send back string
return JSON.stringify({ code, map });
}
compile().then(console.log).catch(err => {
console.error('Compile Error:', err);
process.exit(1);
}); This should presumably work with any require hook. Only the That said, the "official" |
I've always found mine consistent too, but apparently it isn't. 'Works on my machine' probably. |
It probably is! High chance I was doing something stupid. |
Probably isn't! Wasn't a defence, more just pointing out that the approach could well be inconsistent. |
I tried this out in sveltejs/svelte#5770 and it seems to work. Supporting both async and sync makes the code a bit messy, though. What do you think, is this a way forward? |
This feels like the wrong solution honestly. We should find a way to test the result of the snowpack pipeline (since a component might very well rely on modules that also need to be processed by snowpack) rather than incomplete scenario-solving |
Jest just merged support for async transforms, which means pre-processing without creating new processes will now be supported with the next release of Jest |
I'm not entirely sure i understand what this issue is about but some thoughts crossed my mind that i'd like to share in hopes they will be helpful
ps. that jest setup is mostly not my work. i adpoted it from vite and added some things like the guessing part |
This could be useful: https://github.com/microsoft/playwright-test |
(Sorry… I just realized I was auto-commenting on this ticket from my forked-repo commits. Ignore the first two above – I overwrote my history thinking it wouldn't be viewed by anyone else.) I've had good experience using Modern Web's @web/test-runner with Svelte projects using both the Vite and Snowpack plugins. I created a POC SvelteKit plugin to see if a similar approach would work – this is still rough around the edges, but it demonstrates the basic idea: kenkunz/sveltekit 1d99ee1. To try it out, install the kenkunz/sveltekit, use I included sample tests for both a page component (that includes nested components) and a There are several benefits to this approach that align with the concerns I saw raised in this thread:
If you think this approach has merit, I would gladly work on turning it into a releasable version. Per my code comments, I think we'd want to extract & export a method from SvelteKit to start a dev server (from JS vs. cli), then create a real WTR SvelteKit plugin as a separate package. If you don't think Web Test Runner is the right fit, please share some feedback as to why – I'm open to exploring alternatives. I think providing a working and flexible off-the-shelf testing solution would help with SvelteKit adoption. Personally, this was a potential barrier – having a proven/working testing solution has given me confidence to move forward with SvelteKit on my current project. |
I've been converting a component over to SvelteKit and as Rich's original post states, I had a question. 'how do we add tests?' As Rich said last year:
It might be as simple as being clear in the docs/FAQ that kit doesn't have opinions on tests (yet). Next.js just has a section about how to add tests using popular solutions, we could have something similar. Otherwise we could find the new most asked question is about tests and no longer about routers 😄 |
I think Svelte Adders are a pretty good solution for this. There's one for Jest already and folks could create new ones for other testing solutions |
@benmccann thanks I'm still leaning towards SvelteKit having an 'official' way of doing things though. It is after all the official framework for Svelte. Maybe just some basic examples in the docs and then let power users discover tools like |
I think the problem with having "an official way" to do testing is that even among the maintainers we all have our favorite libraries and it will be hard to get consensus. We do have an FAQ which mentions Svelte Adders. Perhaps we could make one specifically for testing if that'd make it more discoverable There's also an FAQ on the main Svelte site about testing: https://svelte.dev/faq#how-do-i-test-svelte-apps |
Yes that's why I'm suggesting adding some examples on the official docs and not choose an official approach, sorry I wasn't very clear in my previous response. So when people look for a testing solution for SvelteKit the docs can show some examples, including a section on using |
One thing that Svelte-Kit probably needs to provide, or at least the FAQ should give recipes for, is some way to mock I wonder if Svelte-Kit could provide a way to tell the compiler "Here's a mock Another subtlety around mocking Just off-the-top-of-my-head ideas at this point. |
Hi All, thanks for the great code and community. I've come up with a new, simple, fast, more reliable way to test my SvelteKit site, using only:
Ideally, this will make for a new svelte-adder, somewhat like svelte-add-jest. I don’t have time to do this right now, but hope to later, and wouldn’t mind if someone else beats me to it. DemoInstall a fresh SvelteKit demo app: npm init svelte@next my-app # demo app, +typescript, +eslint, +prettier
cd my-app
npm install Install our additional packages: npm install --save-dev \
@testing-library/svelte \
c8 \
dotenv \
esm-loader-css \
esm-loader-import-alias \
esm-loader-import-meta-custom \
esm-loader-import-relative-add-extension \
esm-loader-json \
esm-loader-mock-exports \
esm-loader-svelte \
esm-loader-typescript \
global-jsdom \
jsdom \
node-esm-loader \
node-fetch \
uvu Create a // .loaderrc.js
import dotenv from "dotenv";
import { resolve } from "path";
dotenv.config({ path: "./.env.development" });
export default {
loaders: [
"esm-loader-svelte",
"esm-loader-typescript",
"esm-loader-css",
"esm-loader-json",
{
loader: "esm-loader-import-alias",
options: {
aliases: {
"$app/env": resolve(".svelte-kit/runtime/app/env.js"),
"$app/navigation": resolve(".svelte-kit/runtime/app/navigation.js"),
"$app/paths": resolve(".svelte-kit/runtime/app/paths.js"),
"$app/stores": resolve(".svelte-kit/runtime/app/stores.js"),
"$lib/": `${resolve("src/lib/")}/`,
"$service-worker": resolve("src/service-worker.js"),
},
},
},
{
loader: "esm-loader-import-meta-custom",
options: {
meta: {
env: process.env,
},
},
},
{
loader: "esm-loader-import-relative-add-extension",
options: {
extensions: {
".js": ".js",
".svelte": ".ts",
".ts": ".ts",
},
},
},
{
loader: "esm-loader-mock-exports",
options: {
includes: [/svelte\/motion/],
},
},
],
}; We'll add a new test for the // src/lib/Counter.test.ts
import * as assert from "uvu/assert";
import { cleanup, fireEvent, render } from "@testing-library/svelte";
import Counter from "./Counter.svelte";
import { _MOCK } from "svelte/motion";
import { suite } from "uvu";
import { writable } from "svelte/store";
import "global-jsdom/register";
const test = suite("Counter");
test("render", async () => {
_MOCK("spring", writable); // mock spring motion for jsdom
const { findByText, getByLabelText, queryByText } = render(Counter);
const buttonAdd = getByLabelText("Increase the counter by one");
const buttonSub = getByLabelText("Decrease the counter by one");
assert.ok(buttonAdd);
assert.ok(buttonSub);
assert.ok(queryByText("0"));
assert.not(queryByText("2"));
await fireEvent.click(buttonAdd); // 1
await fireEvent.click(buttonAdd); // 2
await fireEvent.click(buttonAdd); // 3
await fireEvent.click(buttonSub); // 2
assert.ok(await findByText("2"));
_MOCK.CLEAR();
cleanup();
});
test.run(); And, we'll add a new test for the // src/routes/todos/index.test.ts
import * as assert from "uvu/assert";
import fetch from "node-fetch";
import { get } from "./index";
import { suite } from "uvu";
globalThis.fetch = fetch;
const test = suite("TodosEndpoint");
test("get", async () => {
globalThis.fetch = async () => ({
ok: true,
status: 200,
json: async () => [
{
uid: "f3fafa29-31ec-41e5-8258-4af5ce938ed0",
text: "hello",
done: true,
},
{
uid: "ad794a97-4f91-4861-a5ab-a52c657822b3",
text: "hi",
done: false,
},
],
});
const result = await get({
request: {},
locals: {
userid: 1234,
},
});
assert.ok(result);
assert.ok(result.body);
assert.ok(result.body.todos);
assert.is(result.body.todos.length, 2);
globalThis.fetch = fetch;
});
test.run(); Finally, we run our new demo tests: NODE_OPTIONS='--experimental-loader node-esm-loader' npx uvu src .test.ts Setup C8 code coverage report config file // .c8rc.json
{
"all": true,
"exclude": [".svelte-kit/**", "**/*.d.ts", "**/*.test.ts"],
"extension": [".js", ".cjs", ".ts", ".svelte"],
"reporter": ["text", "html"],
"src": "src/"
} Generate code coverage reports into NODE_OPTIONS='--experimental-loader node-esm-loader' npx c8 uvu src .test.ts Notes
Thanks for any feedback! cc @jerrythomas |
closed via #4056 |
I found this thread by looking for a playwright usage with sveltekit example. As of today, if one As Rich said:
Although teaching how to use Playwright is out of scope of what the svelte community should provide, wouldn't it be helpful to point to a good working test example of a svelte-kit app? If someone can point me some good example I would happily contribute by adding tests to the demo app. |
@fredguth it's not clear to me what you were hoping would be tested. We demonstrate how to setup and configure things, which is the hard part. What exactly is missing? |
As someone who has never seen Playwright, I was expecting |
If the app template doesn't include tests, then one of the first questions people will ask is 'how do we add tests'? We should probably have some way of answering this question
The text was updated successfully, but these errors were encountered: