Skip to content

Commit

Permalink
chore(e2e): Web example e2e testing (#1162)
Browse files Browse the repository at this point in the history
Added basic e2e tests for the web examples using
[Playwright](https://playwright.dev/) and added a CI workflow with a
matrix for the web projects. The idea is that the same workflow will
also handle the mobile and desktop projects as well.

There is a certain level of duplication for these tests but I think it's
better to keep them isolated per project/example as each one might have
it's own specific requirements - the alternative would be to have yet
another project called something like "examples-e2e" with the e2e logic
(which _should_ be shared among all canonical examples) and then just
checkout and run the projects and test against them.

We can sort out those details as we go but I think it's worth adding
_something_ that's testing them right now.

I've taken care to remove the e2e logic from the starter templates in
the `create-electric-app` CLI and used the opportunity to fix an issue
with configuring ports and add more e2e tests for it (including
interactivity e2e test).

---------

Co-authored-by: Ilia Borovitinov <ilia@electric-sql.com>
  • Loading branch information
msfstef and icehaunter committed Apr 18, 2024
1 parent 572b81c commit ccd7e69
Show file tree
Hide file tree
Showing 20 changed files with 821 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-fans-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-electric-app": patch
---

Fix port configuration not setting proper `.env` variables and update `README`
50 changes: 50 additions & 0 deletions .github/workflows/examples_e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Examples / E2E

on:
workflow_call:
push:
branches:
- main
pull_request:
paths:
- 'examples/**'
- '!examples/**/**.md'

concurrency:
group: examples-e2e-${{ github.ref }}
cancel-in-progress: true

jobs:
test_web:
timeout-minutes: 30
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example_dir: ['examples/web-wa-sqlite', 'examples/web-wa-sqlite-vuejs']
defaults:
run:
working-directory: ${{ matrix.example_dir }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
with:
version: 8
- uses: actions/setup-node@v4
with:
node-version: 18
cache: pnpm
- name: Install dependencies
run: npm ci
- name: Start backend, run migrations, generate client
run: |
npm run backend:up &&
npm run db:migrate &&
npm run client:generate
- name: Start dev server in background
run: npm run dev < /dev/null &
- name: Run e2e tests
working-directory: 'examples/_testing'
run: |
pnpm install --frozen-lockfile &&
pnpm web-e2e
4 changes: 2 additions & 2 deletions .github/workflows/starter_cli_e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ on:
pull_request:
paths:
- 'pnpm-lock.yaml'
- 'examples/starter/**'
- '!examples/starter/**.md'
- 'examples/**'
- '!examples/**/**.md'

defaults:
run:
Expand Down
34 changes: 34 additions & 0 deletions examples/_testing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# E2E testing files
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

# Env files
.env.local
.env.*.local

44 changes: 44 additions & 0 deletions examples/_testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

# ElectricSQL Example Tests

This project contains shared code for testing the ElectricSQL example apps.

Most examples test integrations with different SQLite drivers or view frameworks, but all of them are ultimately the same canonical applicaton for adding and removing items.

## Pre-reqs

- Docker (with Compose V2)
- Node >= 18.0.0

## Usage

### Web Examples

For web projects we use [Playwright](https://playwright.dev/) to run the end-to-end tests.

First run the example application you want to test, e.g.:
```sh
cd ../web-wa-sqlite # or any other web example

# install project dependencies
npm install

# start the backend, migrate, and generate client
npm run backend:up
npm run db:migrate
npm run client:generate

# start dev server in the background
npm run dev < /dev/null &
```

Then run the end-to-end tests:
```sh
pnpm web-e2e
```

This should install the requried browser binaries and spin them up in headless mode to run the tests.

You can configure what browsers to test and other specifics in the `playwright.config.ts` configuration file. Check out the [documentation](https://playwright.dev/docs/test-configuration) for more details.

Note that we set the tests to run serially for each browser by setting `workers` to 1 as the test will use a shared Electric instance that will sync items between the browsers and fail the tests.
65 changes: 65 additions & 0 deletions examples/_testing/e2e/web/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { test, expect } from '@playwright/test'

test.describe('basic example', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/')
})

test('has title and buttons', async ({ page }) => {
await expect(page).toHaveTitle(/Web Example/)
await expect(page.getByRole('button', { name: 'Add' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Clear' })).toBeVisible()

// clear items for good measure
await page.getByRole('button', { name: 'Clear' }).click()
})

test('can add items', async ({ page }) => {
const addButton = page.getByRole('button', { name: 'Add' })
const items = page.getByRole('code')

// no items initially
await expect(await items.count()).toBe(0)

// possible to add an item
await addButton.click()
await page.waitForTimeout(300)
await expect(await items.count()).toBe(1)

// item should contain UUID (not be empty)
await expect(items.first()).toContainText(
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
)

// possible to add more
await addButton.click()
await page.waitForTimeout(300)
await addButton.click()
await page.waitForTimeout(300)
await expect(await items.count()).toBe(3)

// added items persist reload
await page.reload()
await page.waitForTimeout(300)
await expect(await items.count()).toBe(3)
})

test('can clear items', async ({ page }) => {
const clearButton = page.getByRole('button', { name: 'Clear' })
const items = page.getByRole('code')

// should have some items
await page.waitForSelector('code')
await expect(await items.count()).toBeGreaterThan(0)

// should be able to clear them
await clearButton.click()
await page.waitForTimeout(300)
await expect(await items.count()).toBe(0)

// cleared items persist reload
await page.reload()
await page.waitForTimeout(300)
await expect(await items.count()).toBe(0)
})
})
18 changes: 18 additions & 0 deletions examples/_testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "electric-sql-example-tests",
"version": "0.1.0",
"author": "ElectricSQL",
"license": "Apache-2.0",
"type": "module",
"scripts": {
"web-e2e": "npx playwright install --with-deps && npx playwright test"
},
"devDependencies": {
"@playwright/test": "^1.43.1",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^7.7.0",
"@typescript-eslint/parser": "^7.7.0",
"eslint": "^9.0.0",
"typescript": "^5.4.5"
}
}
49 changes: 49 additions & 0 deletions examples/_testing/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { defineConfig, devices } from '@playwright/test'

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './e2e/web',
/* Run tests in files in parallel */
fullyParallel: false,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests to avoid crossing wires between tests */
workers: 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI ? 'dot' : 'line',

/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:5173',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
})
16 changes: 16 additions & 0 deletions examples/_testing/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["e2e/**/*.ts, playwright.config.ts"]
}
10 changes: 5 additions & 5 deletions examples/starter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ Change directory into the created folder (`./my-app` in the example command abov

You can optionally pass the following arguments to the `create-electric-app` command to configure the app.

| Argument | Value | Default | Description
|-----------------------|-------------------------|-----------|--------------
| `--template` | `'react' \| 'vue' \| 'expo' \| 'react-native'` | `'react'` | Starter template to use
| `--electricPort` | `0 - 65535` | `5133` | Port on which to run Electric
| `--electricProxyPort` | `0 - 65535` | `65432` | Port on which to run Electric's DB proxy
| Argument | Value | Default | Description
|-------------------------|-------------------------|-----------|--------------
| `--template` | `'react' \| 'vue' \| 'expo' \| 'react-native'` | `'react'` | Starter template to use
| `--electric-port` | `0 - 65535` | `5133` | Port on which to run Electric
| `--electric-proxy-port` | `0 - 65535` | `65432` | Port on which to run Electric's DB proxy

0 comments on commit ccd7e69

Please sign in to comment.