Skip to content

Commit 8c1a863

Browse files
tomoamRich-Harris
andauthored
fix: restart Vite if tutorial has a .env file (sveltejs#207)
* fix timed out error * Revert "fix timed out error" This reverts commit 3632ee6. * add test for tutorial with a .env file * fix: restart Vite if tutorial has a .env file * Update index.js --------- Co-authored-by: Rich Harris <[email protected]>
1 parent 97408be commit 8c1a863

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/lib/client/adapters/webcontainer/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ export async function create(stubs, callback) {
116116

117117
let added_new_file = false;
118118

119+
const previous_env = /** @type {import('$lib/types').FileStub=} */ (
120+
current_stubs.get('/.env')
121+
);
122+
119123
/** @type {import('$lib/types').Stub[]} */
120124
const to_write = [];
121125

@@ -175,6 +179,15 @@ export async function create(stubs, callback) {
175179
await vm.fs.rm(file, { force: true, recursive: true });
176180
}
177181

182+
// Adding a `.env` file does not restart Vite, but environment variables from `.env`
183+
// are not available until Vite is restarted. By creating a dummy `.env` file, it will
184+
// be recognized as changed when the real `.env` file is loaded into the Webcontainer.
185+
// This will invoke a restart of Vite. Hacky but it works.
186+
// TODO: remove when https://github.com/vitejs/vite/issues/12127 is closed
187+
if (!previous_env && current_stubs.has('/.env')) {
188+
await vm.run({ command: 'touch', args: ['.env']});
189+
}
190+
178191
await vm.loadFiles(convert_stubs_to_tree(to_write));
179192
await promise;
180193
await new Promise((f) => setTimeout(f, 200)); // wait for chokidar

tests/env_file.spec.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { expect, test, chromium } from '@playwright/test';
2+
3+
const chromium_flags = ['--enable-features=SharedArrayBuffer'];
4+
5+
const iframe_selector = 'iframe[src*="webcontainer.io/"]';
6+
7+
test('.env file: no timeout error occurs when switching a tutorials without a .env file to one with it', async () => {
8+
const context = await chromium.launchPersistentContext('', { args: chromium_flags });
9+
const page = context.pages()[0];
10+
await page.bringToFront();
11+
12+
await page.goto('/tutorial/welcome-to-svelte');
13+
14+
const iframe_locator = page.frameLocator(iframe_selector);
15+
16+
// wait for the iframe to load
17+
await iframe_locator.getByText('Welcome!').waitFor();
18+
19+
// switch to another tutorial with a .env file
20+
await page.click('header > h1', { delay: 200 });
21+
await page.locator('button', { hasText: 'Part 4: Advanced SvelteKit' }).click({ delay: 200});
22+
await page.locator('button', { hasText: 'Environment variables' }).click({ delay: 200});
23+
await page.locator('a', { hasText: '$env/static/private' }).click({ delay: 200});
24+
25+
// wait for the iframe to load
26+
await iframe_locator.getByText('enter the passphrase').waitFor();
27+
28+
// wait for a bit, because when Vite dev server is restarted, learn.svelte.dev
29+
// will wait for 10 seconds, after which time a timeout error will occur.
30+
await page.waitForTimeout(11000);
31+
32+
// expect no timeout error
33+
await expect(page.getByText('Yikes!')).toBeHidden();
34+
await expect(iframe_locator.getByText('enter the passphrase')).toBeVisible();
35+
36+
await context.close();
37+
});
38+
39+
test('.env file: environment variables are available when switching a tutorial without a .env file to one with it', async () => {
40+
const context = await chromium.launchPersistentContext('', { args: chromium_flags });
41+
const page = context.pages()[0];
42+
await page.bringToFront();
43+
44+
await page.goto('/tutorial/welcome-to-svelte');
45+
46+
const iframe_locator = page.frameLocator(iframe_selector);
47+
48+
// wait for the iframe to load
49+
await iframe_locator.getByText('Welcome!').waitFor();
50+
51+
// switch to another tutorial with a .env file
52+
await page.click('header > h1', { delay: 200 });
53+
await page.locator('button', { hasText: 'Part 4: Advanced SvelteKit' }).click({ delay: 200});
54+
await page.locator('button', { hasText: 'Environment variables' }).click({ delay: 200});
55+
await page.locator('a', { hasText: '$env/dynamic/private' }).click({ delay: 200});
56+
57+
// wait for the iframe to load
58+
await iframe_locator.getByText('enter the passphrase').waitFor();
59+
await iframe_locator.locator('input[name="passphrase"]').waitFor();
60+
61+
await page.waitForTimeout(500);
62+
63+
// login
64+
// 'open sesame' is the environment variables loaded from `.env` file
65+
await iframe_locator.locator('input[name="passphrase"]').fill('open sesame');
66+
await page.keyboard.press('Enter', { delay: 200 });
67+
68+
// expect to be able to login.
69+
// Being able to log in means that environment variables are loaded.
70+
await expect(iframe_locator.getByText('wrong passphrase!')).toBeHidden();
71+
await expect(iframe_locator.locator('button', { hasText: 'log out'})).toBeEnabled();
72+
73+
await context.close();
74+
});

0 commit comments

Comments
 (0)