Skip to content

Commit d30b5a4

Browse files
committed
docs: readme and jsdocs
1 parent 5c37cd2 commit d30b5a4

File tree

8 files changed

+164
-7
lines changed

8 files changed

+164
-7
lines changed

README.md

Lines changed: 56 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`
@@ -102,5 +110,53 @@ await webcontainer.runCommand("npm", ["install"]);
102110
const files = await webcontainer.runCommand("ls", ["-l"]);
103111
```
104112

113+
##### `readFile`
114+
115+
WebContainer's [`readFile`](https://webcontainers.io/guides/working-with-the-file-system#readfile) method.
116+
117+
```ts
118+
const content = await webcontainer.readFile("/package.json");
119+
```
120+
121+
##### `writeFile`
122+
123+
WebContainer's [`writeFile`](https://webcontainers.io/guides/working-with-the-file-system#writefile) method.
124+
125+
```ts
126+
await webcontainer.writeFile("/main.ts", "console.log('Hello world!')");
127+
```
128+
129+
##### `rename`
130+
131+
WebContainer's [`rename`](https://webcontainers.io/guides/working-with-the-file-system#rename) method.
132+
133+
```ts
134+
await webcontainer.rename("/before.ts", "/after.ts");
135+
```
136+
137+
##### `mkdir`
138+
139+
WebContainer's [`mkdir`](https://webcontainers.io/guides/working-with-the-file-system#mkdir) method.
140+
141+
```ts
142+
await webcontainer.mkdir("/src/components");
143+
```
144+
145+
##### `readdir`
146+
147+
WebContainer's [`readdir`](https://webcontainers.io/guides/working-with-the-file-system#readdir) method.
148+
149+
```ts
150+
const contents = await webcontainer.readdir("/src");
151+
```
152+
153+
##### `rm`
154+
155+
WebContainer's [`rm`](https://webcontainers.io/guides/working-with-the-file-system#rm) method.
156+
157+
```ts
158+
await webcontainer.rm("/node_modules");
159+
```
160+
105161
[version-badge]: https://img.shields.io/npm/v/@webcontainer/test
106162
[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: 36 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,43 @@ 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": { file: { contents: 'export default "Hello world";' } },
31+
nested: {
32+
directory: {
33+
"file-2.ts": {
34+
file: { contents: 'export default "Hello from nested file";' },
35+
},
36+
},
37+
},
38+
});
39+
40+
const ls = await webcontainer.runCommand("ls");
41+
expect(ls).toMatchInlineSnapshot(`"file-1.ts nested"`);
42+
43+
const lsNested = await webcontainer.runCommand("ls", ["nested"]);
44+
expect(lsNested).toMatchInlineSnapshot(`"file-2.ts"`);
45+
46+
const catFile = await webcontainer.runCommand("cat", ["file-1.ts"]);
47+
expect(catFile).toMatchInlineSnapshot(`"export default "Hello world";"`);
48+
49+
const catNestedFile = await webcontainer.runCommand("cat", [
50+
"nested/file-2.ts",
51+
]);
52+
expect(catNestedFile).toMatchInlineSnapshot(
53+
`"export default "Hello from nested file";"`,
54+
);
55+
});
56+
2457
test("user should see error when attemping to mount files outside project root", async ({
2558
webcontainer,
2659
}) => {
2760
await expect(() => webcontainer.mount("/home/non-existing")).rejects
2861
.toThrowErrorMatchingInlineSnapshot(`
29-
[Error: [vitest:webcontainers] Cannot read files outside project root:
62+
[Error: [vitest:webcontainers] Cannot read files outside project root:
3063
{
3164
"directory": "/home/non-existing",
3265
"resolved": "/home/non-existing"
@@ -35,7 +68,7 @@ test("user should see error when attemping to mount files outside project root",
3568

3669
await expect(() => webcontainer.mount("/../../non-existing")).rejects
3770
.toThrowErrorMatchingInlineSnapshot(`
38-
[Error: [vitest:webcontainers] Cannot read files outside project root:
71+
[Error: [vitest:webcontainers] Cannot read files outside project root:
3972
{
4073
"directory": "/../../non-existing",
4174
"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)