How to test actions?

I'm using vitest in a SolidStart project. I'm trying to create tests for an action and haven't been able to figure it out or find any info on it. My current guess is that I need to automate/UI Test a form in a route, but thought I would ask before using more time by guessing. I have never done UI testing and wishing there was a way to do this in pure JS. But useAction only works in a Route so I'm guessing creating a route with a form is the only way?
2 Replies
Madaxen86
Madaxen862mo ago
This is what I came up with:
async function awaitPromise(timeout: number = 1) {
return new Promise(resolve => setTimeout(resolve, timeout));
}


const fakeDB = [
{
id: 1
}
];

const getData = query(async () => [...fakeDB], "getData");

const mutation = action(async () => {
fakeDB.push({ id: fakeDB.length });
reload({ revalidate: getData.key });
});

function Component() {
const data = createAsync(() => getData());
const mutate = useAction(mutation);
return (
<Suspense>
<div id="data">{data()?.length}</div>
<div id="json">{JSON.stringify(data())}</div>
<button id="btn" onClick={mutate} />
<form method="post" action={mutation}>
<input id="submit" type="submit" />
</form>
</Suspense>
);
}

function App() {
return (
<Router>
<Route path="/" component={Component} />
</Router>
);
}

describe(`action and query`, () => {
afterEach(() => {
document.body.innerHTML = "";
});
test(`init`, () =>
createRoot(async dispose => {
render(() => <App />, document.body);
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("1");

document.getElementById("btn")?.click();
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("2");

document.getElementById("submit")?.click();
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("3");
dispose();
}));
});
async function awaitPromise(timeout: number = 1) {
return new Promise(resolve => setTimeout(resolve, timeout));
}


const fakeDB = [
{
id: 1
}
];

const getData = query(async () => [...fakeDB], "getData");

const mutation = action(async () => {
fakeDB.push({ id: fakeDB.length });
reload({ revalidate: getData.key });
});

function Component() {
const data = createAsync(() => getData());
const mutate = useAction(mutation);
return (
<Suspense>
<div id="data">{data()?.length}</div>
<div id="json">{JSON.stringify(data())}</div>
<button id="btn" onClick={mutate} />
<form method="post" action={mutation}>
<input id="submit" type="submit" />
</form>
</Suspense>
);
}

function App() {
return (
<Router>
<Route path="/" component={Component} />
</Router>
);
}

describe(`action and query`, () => {
afterEach(() => {
document.body.innerHTML = "";
});
test(`init`, () =>
createRoot(async dispose => {
render(() => <App />, document.body);
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("1");

document.getElementById("btn")?.click();
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("2");

document.getElementById("submit")?.click();
await awaitPromise();
expect(document.getElementById("data")?.textContent).toBe("3");
dispose();
}));
});
As an alternative. If you extract the function inside the action you could test this and assume it'll work with the action as well. In my example I test the whole cycle of query -> action -> revalidate the query . I don't thinks its to crazy UI testing.
Carl (klequis)
Carl (klequis)OP2mo ago
That was hugely helpful. There were a couple of things I hadn't thought about - Can't use <FileRoutes> - UI isn't actually rendered I think it would be good to add your code to the official example: https://github.com/solidjs/solid-start/tree/main/examples/with-vitest ? Here it is as an example project with the way you did it and a variation moving the db and query/action to /lib to simulate the project I'm working on. I originally tried the latter with <FileRoutes> and got a bunch of very strange errors.
https://github.com/klequis/ex-madaxen-query-actions.

Did you find this page helpful?