Skip to content

Commit 99c0ec1

Browse files
authored
docs: readme and jsdocs (#6)
1 parent 5c37cd2 commit 99c0ec1

File tree

8 files changed

+175
-7
lines changed

8 files changed

+175
-7
lines changed

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ Vitest's [`getByText`](https://vitest.dev/guide/browser/locators.html#getbytext)
8080
await preview.getByText("Hello Vite!");
8181
```
8282

83+
##### `locator`
84+
85+
Vitest's [`locator`](https://vitest.dev/guide/browser/locators.html) of the preview window.
86+
87+
```ts
88+
await preview.locator.hover();
89+
```
90+
8391
#### `webcontainer`
8492

8593
##### `mount`
@@ -90,6 +98,15 @@ Accepts a path that is relative to the [project root](https://vitest.dev/config/
9098

9199
```ts
92100
await webcontainer.mount("/path/to/project");
101+
102+
await webcontainer.mount({
103+
"package.json": { file: { contents: '{ "name": "example-project" }' } },
104+
src: {
105+
directory: {
106+
"index.ts": { file: { contents: "export default 'Hello!';" } },
107+
},
108+
},
109+
});
93110
```
94111

95112
##### `runCommand`
@@ -102,5 +119,53 @@ await webcontainer.runCommand("npm", ["install"]);
102119
const files = await webcontainer.runCommand("ls", ["-l"]);
103120
```
104121

122+
##### `readFile`
123+
124+
WebContainer's [`readFile`](https://webcontainers.io/guides/working-with-the-file-system#readfile) method.
125+
126+
```ts
127+
const content = await webcontainer.readFile("/package.json");
128+
```
129+
130+
##### `writeFile`
131+
132+
WebContainer's [`writeFile`](https://webcontainers.io/guides/working-with-the-file-system#writefile) method.
133+
134+
```ts
135+
await webcontainer.writeFile("/main.ts", "console.log('Hello world!')");
136+
```
137+
138+
##### `rename`
139+
140+
WebContainer's [`rename`](https://webcontainers.io/guides/working-with-the-file-system#rename) method.
141+
142+
```ts
143+
await webcontainer.rename("/before.ts", "/after.ts");
144+
```
145+
146+
##### `mkdir`
147+
148+
WebContainer's [`mkdir`](https://webcontainers.io/guides/working-with-the-file-system#mkdir) method.
149+
150+
```ts
151+
await webcontainer.mkdir("/src/components");
152+
```
153+
154+
##### `readdir`
155+
156+
WebContainer's [`readdir`](https://webcontainers.io/guides/working-with-the-file-system#readdir) method.
157+
158+
```ts
159+
const contents = await webcontainer.readdir("/src");
160+
```
161+
162+
##### `rm`
163+
164+
WebContainer's [`rm`](https://webcontainers.io/guides/working-with-the-file-system#rm) method.
165+
166+
```ts
167+
await webcontainer.rm("/node_modules");
168+
```
169+
105170
[version-badge]: https://img.shields.io/npm/v/@webcontainer/test
106171
[npm-url]: https://www.npmjs.com/package/@webcontainer/test

src/commands/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const readDirectory: BrowserCommand<[directory: string]> = async (
1313

1414
if (!resolved.startsWith(root)) {
1515
throw new Error(
16-
`[vitest:webcontainers] Cannot read files outside project root: \n${JSON.stringify(
16+
`[vitest:webcontainers] Cannot read files outside project root:\n${JSON.stringify(
1717
{ directory, resolved },
1818
null,
1919
2,

src/fixtures/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@ import { test as base } from "vitest";
33
import { Preview } from "./preview";
44
import { WebContainer } from "./webcontainer";
55

6+
/**
7+
* Pre-defined [`test()` function](https://vitest.dev/guide/test-context.html#extend-test-context) with WebContainer fixtures.
8+
*
9+
* @example
10+
* ```ts
11+
* import { test } from "@webcontainer/test";
12+
*
13+
* test("run development server inside webcontainer", async ({
14+
* webcontainer,
15+
* preview,
16+
* }) => {
17+
* await webcontainer.mount("path/to/project");
18+
*
19+
* await webcontainer.runCommand("npm", ["install"]);
20+
* webcontainer.runCommand("npm", ["run", "dev"]);
21+
*
22+
* await preview.getByRole("heading", { level: 1, name: "Hello Vite!" });
23+
* });
24+
* ```
25+
*/
626
export const test = base.extend<{
727
preview: Preview;
828
webcontainer: WebContainer;

src/fixtures/preview.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ import { type Locator, page } from "@vitest/browser/context";
33
const TEST_ID = "webcontainers-iframe";
44

55
export class Preview {
6+
/** @internal */
67
private _preview: Locator;
8+
9+
/** @internal */
710
private _iframe?: HTMLIFrameElement;
811

912
constructor() {
1013
this._preview = page.getByTestId(TEST_ID);
1114
}
1215

16+
/** @internal */
1317
async setup(url: string) {
1418
const iframe = document.createElement("iframe");
1519
iframe.setAttribute("src", url);
@@ -21,17 +25,31 @@ export class Preview {
2125
this._iframe = iframe;
2226
}
2327

28+
/** @internal */
2429
async teardown() {
2530
if (this._iframe) {
2631
document.body.removeChild(this._iframe);
2732
}
2833
}
2934

35+
/**
36+
* Vitest's [`getByRole`](https://vitest.dev/guide/browser/locators.html#getbyrole) that's scoped to the preview window.
37+
*/
3038
async getByRole(...options: Parameters<typeof page.getByRole>) {
3139
return this._preview.getByRole(...options);
3240
}
3341

42+
/**
43+
* Vitest's [`getByText`](https://vitest.dev/guide/browser/locators.html#getbytext) that's scoped to the preview window.
44+
*/
3445
async getByText(...options: Parameters<typeof page.getByText>) {
3546
return this._preview.getByText(...options);
3647
}
48+
49+
/**
50+
* Vitest's [`locator`](https://vitest.dev/guide/browser/locators.html) of the preview window.
51+
*/
52+
get locator() {
53+
return this._preview;
54+
}
3755
}

src/fixtures/webcontainer.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import {
66
} from "@webcontainer/api";
77

88
export class WebContainer {
9+
/** @internal */
910
private _instancePromise?: WebContainerApi;
11+
12+
/** @internal */
1013
private _isReady: Promise<void>;
14+
15+
/** @internal */
1116
private _onExit: (() => Promise<unknown>)[] = [];
1217

1318
constructor() {
@@ -26,18 +31,20 @@ export class WebContainer {
2631
return this._instancePromise;
2732
}
2833

34+
/** @internal */
2935
async wait() {
3036
await this._isReady;
3137
}
3238

39+
/** @internal */
3340
onServerReady(callback: (options: { port: number; url: string }) => void) {
3441
this._instance.on("server-ready", (port, url) => {
3542
callback({ port, url });
3643
});
3744
}
3845

3946
/**
40-
* Mount file directory into webcontainer.
47+
* Mount file directory into WebContainer.
4148
* `string` arguments are considered paths that are relative to [`root`](https://vitest.dev/config/#root)
4249
*/
4350
async mount(filesOrPath: string | FileSystemTree) {
@@ -48,6 +55,7 @@ export class WebContainer {
4855
return await this._instance.mount(filesOrPath as FileSystemTree);
4956
}
5057

58+
/** @internal */
5159
async teardown() {
5260
await Promise.all(this._onExit.map((fn) => fn()));
5361

@@ -58,6 +66,10 @@ export class WebContainer {
5866
this._instancePromise = undefined;
5967
}
6068

69+
/**
70+
* Run command inside WebContainer.
71+
* Returns the output of the command.
72+
*/
6173
async runCommand(command: string, args: string[] = []) {
6274
let output = "";
6375

@@ -86,26 +98,44 @@ export class WebContainer {
8698
return output.trim();
8799
}
88100

101+
/**
102+
* WebContainer's [`readFile`](https://webcontainers.io/guides/working-with-the-file-system#readfile) method.
103+
*/
89104
async readFile(path: string, encoding: BufferEncoding = "utf8") {
90105
return this._instance.fs.readFile(path, encoding);
91106
}
92107

108+
/**
109+
* WebContainer's [`writeFile`](https://webcontainers.io/guides/working-with-the-file-system#writefile) method.
110+
*/
93111
async writeFile(path: string, data: string, encoding = "utf8") {
94112
return this._instance.fs.writeFile(path, data, { encoding });
95113
}
96114

115+
/**
116+
* WebContainer's [`rename`](https://webcontainers.io/guides/working-with-the-file-system#rename) method.
117+
*/
97118
async rename(oldPath: string, newPath: string) {
98119
return this._instance.fs.rename(oldPath, newPath);
99120
}
100121

122+
/**
123+
* WebContainer's [`mkdir`](https://webcontainers.io/guides/working-with-the-file-system#mkdir) method.
124+
*/
101125
async mkdir(path: string) {
102126
return this._instance.fs.mkdir(path);
103127
}
104128

129+
/**
130+
* WebContainer's [`readdir`](https://webcontainers.io/guides/working-with-the-file-system#readdir) method.
131+
*/
105132
async readdir(path: string) {
106133
return this._instance.fs.readdir(path);
107134
}
108135

136+
/**
137+
* WebContainer's [`rm`](https://webcontainers.io/guides/working-with-the-file-system#rm) method.
138+
*/
109139
async rm(path: string) {
110140
return this._instance.fs.rm(path);
111141
}

src/plugin.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import type { Vite } from "vitest/node";
22
import { readDirectory } from "./commands";
3-
import "./types.d.ts";
43

54
const COEP = "Cross-Origin-Embedder-Policy";
65
const COOP = "Cross-Origin-Opener-Policy";

test/mount.test.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { expect } from "vitest";
22
import { test } from "../src";
33

4-
test("user can mount directories to webcontainer", async ({ webcontainer }) => {
4+
test("user can mount directories from file-system to webcontainer", async ({
5+
webcontainer,
6+
}) => {
57
await webcontainer.mount("test/fixtures/mount-example");
68

79
const ls = await webcontainer.runCommand("ls");
@@ -21,12 +23,45 @@ test("user can mount directories to webcontainer", async ({ webcontainer }) => {
2123
);
2224
});
2325

26+
test("user can mount inlined FileSystemTree to webcontainer", async ({
27+
webcontainer,
28+
}) => {
29+
await webcontainer.mount({
30+
"file-1.ts": {
31+
file: { contents: 'export default "Hello world";' },
32+
},
33+
nested: {
34+
directory: {
35+
"file-2.ts": {
36+
file: { contents: 'export default "Hello from nested file";' },
37+
},
38+
},
39+
},
40+
});
41+
42+
const ls = await webcontainer.runCommand("ls");
43+
expect(ls).toMatchInlineSnapshot(`"file-1.ts nested"`);
44+
45+
const lsNested = await webcontainer.runCommand("ls", ["nested"]);
46+
expect(lsNested).toMatchInlineSnapshot(`"file-2.ts"`);
47+
48+
const catFile = await webcontainer.runCommand("cat", ["file-1.ts"]);
49+
expect(catFile).toMatchInlineSnapshot(`"export default "Hello world";"`);
50+
51+
const catNestedFile = await webcontainer.runCommand("cat", [
52+
"nested/file-2.ts",
53+
]);
54+
expect(catNestedFile).toMatchInlineSnapshot(
55+
`"export default "Hello from nested file";"`,
56+
);
57+
});
58+
2459
test("user should see error when attemping to mount files outside project root", async ({
2560
webcontainer,
2661
}) => {
2762
await expect(() => webcontainer.mount("/home/non-existing")).rejects
2863
.toThrowErrorMatchingInlineSnapshot(`
29-
[Error: [vitest:webcontainers] Cannot read files outside project root:
64+
[Error: [vitest:webcontainers] Cannot read files outside project root:
3065
{
3166
"directory": "/home/non-existing",
3267
"resolved": "/home/non-existing"
@@ -35,7 +70,7 @@ test("user should see error when attemping to mount files outside project root",
3570

3671
await expect(() => webcontainer.mount("/../../non-existing")).rejects
3772
.toThrowErrorMatchingInlineSnapshot(`
38-
[Error: [vitest:webcontainers] Cannot read files outside project root:
73+
[Error: [vitest:webcontainers] Cannot read files outside project root:
3974
{
4075
"directory": "/../../non-existing",
4176
"resolved": "/non-existing"

tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"module": "ESNext",
55
"target": "ESNext",
66
"moduleResolution": "bundler",
7-
"skipLibCheck": true
7+
"skipLibCheck": true,
8+
"stripInternal": true
89
},
910
"include": ["src", "test"]
1011
}

0 commit comments

Comments
 (0)