Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .llms-snapshots/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5001,6 +5001,90 @@ You can either automate your deployment with GitHub Actions (recommended) or dep

[GitHub Actions](/docs/guides/github-actions.md)[Manual](/docs/guides/manual-deployment.md)

# End-to-End Testing

End to end (E2E) testing helps you verify that your application behaves as expected when deployed. It covers real workflows — from serverless functions to client calls — and helps catch issues that unit tests might miss.

This page outlines how to approach E2E testing with Juno. It includes recommendations and patterns.

---

## Frameworks

We suggest [Playwright](https://playwright.dev/), but [Cypress](https://www.cypress.io/) or other frameworks will work.

Choose whatever fits your project best.

That being said, integrating authentication is easier in Playwright given that a plugin is available (see next chapter).

---

## Authentication

If your application require authentication, we recommend using the Playwright plugin for Internet Identity maintained by the DFINITY foundation:

👉 [github.com/dfinity/internet-identity-playwright](https://github.com/dfinity/internet-identity-playwright)

It handles the full login flow programmatically, allowing your tests to sign in without user interaction.

### Example usage

After installing the plugin, you can write a test like this:

```
import { testWithII } from "@dfinity/internet-identity-playwright";testWithII("should sign-in with a new user", async ({ page, iiPage }) => { await page.goto("/"); await iiPage.signInWithNewIdentity();});
```

---

## Continuous Integration

To run end-to-end tests in CI, we recommend using the `junobuild/satellite` image.

This image runs a headless Satellite (spun with a predictable ID `jx5yt-yyaaa-aaaal-abzbq-cai`) with all core services enabled. It's a bit faster to start and does not require using the Console UI.

---

### Configuration

In your `juno.config.ts`, make sure to set the `development` ID to match the one used by the image, and set the emulator to run the `junobuild/satellite` image.

If you're using a different image like Skylab, you can make this configuration dynamic based on the mode received by `defineConfig`.

You can also replace `development` with `test` or any other mode. Adapt as you wish.

```
import { defineConfig } from "@junobuild/config";export default defineConfig({ satellite: { ids: { development: "jx5yt-yyaaa-aaaal-abzbq-cai", production: "<PROD_SATELLITE_ID>" }, source: "out", collections: { datastore: [ { collection: "notes", read: "managed", write: "managed", memory: "stable" } ], storage: [ { collection: "images", read: "managed", write: "managed", memory: "stable" } ] } }, emulator: { runner: { type: "docker" }, satellite: {} }});
```

The configuration above also defines the collections used by the application under test.

In the next chapter, we'll apply this config before running the tests.

---

### GitHub Actions

To run the tests in your CI, you can either use the [GitHub Actions](/docs/guides/github-actions.md) or install the CLI manually. In the example below, we install the CLI because we chain multiple commands.

```
name: E2E Testson: pull_request: workflow_dispatch:jobs: e2e: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v4 with: node-version: 22 registry-url: "https://registry.npmjs.org" - name: Install dependencies run: npm ci - name: Install Juno CLI run: npm i -g @junobuild/cli - name: Run emulator run: | set -e juno dev start --headless & juno dev wait juno login --emulator --mode development --headless juno config --mode development --headless - name: Run tests run: npm run e2e:ci - name: Upload Playwright report on failure uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: playwright-report path: playwright-report/ retention-days: 3 - name: Upload Playwright results on failure uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: test-results path: test-results/ retention-days: 3 may-merge: needs: ["e2e"] runs-on: ubuntu-latest steps: - name: Cleared for merging run: echo OK
```

So, how it works:

* We start by checking out the code, installing Node.js, and running `npm ci` to install your project dependencies.
* As mentioned, instead of the Juno GitHub Actions, we install the Juno CLI globally so we can use it in the next steps.
* We then run the emulator using `juno dev start --headless`, which launches the `junobuild/satellite` Docker image (defined in the `juno.config`) in the background, and follow up with:
* `juno dev wait` to ensure the emulator is ready before continuing.
* `juno login` sets up authentication against the emulator in headless mode. This way the CLI can operate the Satellite — required for the next step.
* `juno config` applies the configuration and sets the collections required by the project.
* Once everything is ready, we run the end-to-end tests via `npm run e2e:ci`. Replace with the command that runs your tests in headless mode.
* If the tests fail, Playwright reports and raw test results are uploaded as artifacts to help debugging.
* Finally, if everything passes, the `may-merge` job marks the PR as cleared.

That's it. Minimal setup, no need for the Console UI, and everything runs headlessly in CI.

# Manual Deployment

We recommend using [GitHub Actions](/docs/guides/github-actions.md) for automated and efficient deployments. However, this guide walks you through manually deploying your app using the Juno CLI, covering the setup, build, and deployment process to your Juno Satellite.
Expand Down
1 change: 1 addition & 0 deletions .llms-snapshots/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Juno is your self-contained serverless platform for building full-stack web apps
- [AI](https://juno.build/docs/guides/ai.md): Learn how to use Juno's llms.txt files to provide AI tools with better context for building serverless functions, deploying satellites, and integrating the SDK.
- [Angular](https://juno.build/docs/guides/angular.md): Use Juno with Angular
- [Astro](https://juno.build/docs/guides/astro.md): Learn how to integrate Juno with Astro for seamless development. Follow our quickstart guide and hosting instructions.
- [End-to-End Testing](https://juno.build/docs/guides/e2e.md): Learn how to write and run end-to-end (E2E) tests for your Juno project using tools like Playwright and GitHub Actions. Includes emulator setup, authentication handling, and CI integration.
- [Manual](https://juno.build/docs/guides/manual-deployment.md): Learn how to deploy an application to a Juno Satellite from your device using the CLI.
- [Next.js](https://juno.build/docs/guides/nextjs.md): Learn how to integrate Juno with Next.js. Follow our quickstart guide and examples to get started.
- [NodeJS](https://juno.build/docs/guides/nodejs.md): SDK usage in a NodeJS context
Expand Down
187 changes: 187 additions & 0 deletions docs/guides/e2e.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
id: e2e
title: End-to-End Testing
description: Learn how to write and run end-to-end (E2E) tests for your Juno project using tools like Playwright and GitHub Actions. Includes emulator setup, authentication handling, and CI integration.
---

# End-to-End Testing

End to end (E2E) testing helps you verify that your application behaves as expected when deployed. It covers real workflows — from serverless functions to client calls — and helps catch issues that unit tests might miss.

This page outlines how to approach E2E testing with Juno. It includes recommendations and patterns.

---

## Frameworks

We suggest [Playwright](https://playwright.dev/), but [Cypress](https://www.cypress.io/) or other frameworks will work.

Choose whatever fits your project best.

That being said, integrating authentication is easier in Playwright given that a plugin is available (see next chapter).

---

## Authentication

If your application require authentication, we recommend using the Playwright plugin for Internet Identity maintained by the DFINITY foundation:

👉 [github.com/dfinity/internet-identity-playwright](https://github.com/dfinity/internet-identity-playwright)

It handles the full login flow programmatically, allowing your tests to sign in without user interaction.

### Example usage

After installing the plugin, you can write a test like this:

```typescript
import { testWithII } from "@dfinity/internet-identity-playwright";

testWithII("should sign-in with a new user", async ({ page, iiPage }) => {
await page.goto("/");

await iiPage.signInWithNewIdentity();
});
```

---

## Continuous Integration

To run end-to-end tests in CI, we recommend using the `junobuild/satellite` image.

This image runs a headless Satellite (spun with a predictable ID `jx5yt-yyaaa-aaaal-abzbq-cai`) with all core services enabled. It's a bit faster to start and does not require using the Console UI.

---

### Configuration

In your `juno.config.ts`, make sure to set the `development` ID to match the one used by the image, and set the emulator to run the `junobuild/satellite` image.

If you're using a different image like Skylab, you can make this configuration dynamic based on the mode received by `defineConfig`.

You can also replace `development` with `test` or any other mode. Adapt as you wish.

```typescript
import { defineConfig } from "@junobuild/config";

export default defineConfig({
satellite: {
ids: {
development: "jx5yt-yyaaa-aaaal-abzbq-cai",
production: "<PROD_SATELLITE_ID>"
},
source: "out",
collections: {
datastore: [
{
collection: "notes",
read: "managed",
write: "managed",
memory: "stable"
}
],
storage: [
{
collection: "images",
read: "managed",
write: "managed",
memory: "stable"
}
]
}
},
emulator: {
runner: {
type: "docker"
},
satellite: {}
}
});
```

The configuration above also defines the collections used by the application under test.

In the next chapter, we'll apply this config before running the tests.

---

### GitHub Actions

To run the tests in your CI, you can either use the [GitHub Actions](./github-actions/index.mdx) or install the CLI manually. In the example below, we install the CLI because we chain multiple commands.

```yaml
name: E2E Tests

on:
pull_request:
workflow_dispatch:

jobs:
e2e:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 22
registry-url: "https://registry.npmjs.org"

- name: Install dependencies
run: npm ci

- name: Install Juno CLI
run: npm i -g @junobuild/cli

- name: Run emulator
run: |
set -e
juno dev start --headless &
juno dev wait
juno login --emulator --mode development --headless
juno config --mode development --headless

- name: Run tests
run: npm run e2e:ci

- name: Upload Playwright report on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 3

- name: Upload Playwright results on failure
uses: actions/upload-artifact@v4
if: ${{ failure() }}
with:
name: test-results
path: test-results/
retention-days: 3

may-merge:
needs: ["e2e"]
runs-on: ubuntu-latest
steps:
- name: Cleared for merging
run: echo OK
```

So, how it works:

- We start by checking out the code, installing Node.js, and running `npm ci` to install your project dependencies.
- As mentioned, instead of the Juno GitHub Actions, we install the Juno CLI globally so we can use it in the next steps.
- We then run the emulator using `juno dev start --headless`, which launches the `junobuild/satellite` Docker image (defined in the `juno.config`) in the background, and follow up with:
- `juno dev wait` to ensure the emulator is ready before continuing.
- `juno login` sets up authentication against the emulator in headless mode. This way the CLI can operate the Satellite — required for the next step.
- `juno config` applies the configuration and sets the collections required by the project.
- Once everything is ready, we run the end-to-end tests via `npm run e2e:ci`. Replace with the command that runs your tests in headless mode.
- If the tests fail, Playwright reports and raw test results are uploaded as artifacts to help debugging.
- Finally, if everything passes, the `may-merge` job marks the PR as cleared.

That's it. Minimal setup, no need for the Console UI, and everything runs headlessly in CI.
1 change: 1 addition & 0 deletions sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const sidebars: SidebarsConfig = {
]
},
"guides/local-development",
"guides/e2e",
"guides/ai"
]
},
Expand Down