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
44 changes: 44 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Browser E2E

on:
pull_request:
branches: [master]

jobs:
playwright:
name: Playwright Chromium
runs-on: ubuntu-latest

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

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
extensions: mbstring, xml, curl, intl, json, sockets, sqlite3
coverage: none

- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: Enable pnpm
run: corepack enable

- name: Install JavaScript dependencies
run: pnpm install --frozen-lockfile

- name: Install Chromium
run: pnpm exec playwright install --with-deps chromium

- name: Run browser tests
run: pnpm e2e

- name: Upload Playwright report
if: failure() && !cancelled()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 14
22 changes: 13 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ writable/session/*
writable/uploads/*
!writable/uploads/index.html

writable/debugbar/*
!writable/debugbar/.gitkeep

writable/database/*
!writable/database/.gitkeep

php_errors.log
writable/debugbar/*
!writable/debugbar/.gitkeep

writable/database/*
!writable/database/.gitkeep

writable/websocket_tokens.json

php_errors.log

# Legacy CI3 paths
application/config/database.php
Expand Down Expand Up @@ -146,8 +148,10 @@ _/
.claude
CLAUDE.md

/results/
/phpunit*.xml
/results/
/phpunit*.xml
/playwright-report/
/test-results/

#-------------------------
# Static Analysis Cache
Expand Down
12 changes: 6 additions & 6 deletions app/Commands/ChatWebSocketServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,42 @@ class ChatWebSocketServer extends BaseCommand
*
* @var string
*/
protected string $group = 'Chat';
protected $group = 'Chat';

/**
* The Command's Name
*
* @var string
*/
protected string $name = 'chat:websocket';
protected $name = 'chat:websocket';

/**
* The Command's Description
*
* @var string
*/
protected string $description = 'Starts the WebSocket server for the chat application';
protected $description = 'Starts the WebSocket server for the chat application';

/**
* The Command's Usage
*
* @var string
*/
protected string $usage = 'chat:websocket [options]';
protected $usage = 'chat:websocket [options]';

/**
* The Command's Arguments
*
* @var array
*/
protected array $arguments = [];
protected $arguments = [];

/**
* The Command's Options
*
* @var array
*/
protected array $options = [
protected $options = [
'--port' => 'Port to run the WebSocket server on (default: 8080)',
];

Expand Down
4 changes: 2 additions & 2 deletions app/Views/chat/chatView.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>CodeIgniter Shoutbox</title>
<meta name="csrf-token" content="<?= esc(csrf_hash()) ?>">
Expand Down Expand Up @@ -28,7 +28,7 @@
<p id="messagewindow"><span id="loading">Loading...</span></p>
<form id="chatform">
<div id="txt">
Message: <input type="text" name="content" id="content" value="" />
<label for="content">Message:</label> <input type="text" name="content" id="content" value="" />
<div id="content-error" class="error"></div>
</div>

Expand Down
6 changes: 3 additions & 3 deletions app/Views/chat/htmlView.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>CodeIgniter Shoutbox</title>
<?php
Expand All @@ -26,12 +26,12 @@
<form id="chatform" action="<?= esc(site_url('chat/update')) ?>" method="post">
<?= csrf_field() ?>
<div id="author">
Name: <input type="text" name="name" id="name" value="<?= esc(old('name')) ?>" />
<label for="name">Name:</label> <input type="text" name="name" id="name" value="<?= esc(old('name')) ?>" />
<div id="name-error" class="error"></div>
</div><br />

<div id="txt">
Message: <input type="text" name="message" id="content" value="<?= esc(old('message')) ?>" />
<label for="content">Message:</label> <input type="text" name="message" id="content" value="<?= esc(old('message')) ?>" />
<div id="content-error" class="error"></div>
</div>

Expand Down
6 changes: 3 additions & 3 deletions app/Views/chat/jsonView.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>CodeIgniter Shoutbox - JSON edition</title>
<meta name="csrf-token" content="<?= esc(csrf_hash()) ?>">
Expand All @@ -23,12 +23,12 @@
<p id="messagewindow"><span id="loading">Loading...</span></p>
<form id="chatform">
<div id="author">
Name: <input type="text" id="name" />
<label for="name">Name:</label> <input type="text" id="name" />
<div id="name-error" class="error"></div>
</div><br />

<div id="txt">
Message: <input type="text" name="content" id="content" value="" />
<label for="content">Message:</label> <input type="text" name="content" id="content" value="" />
<div id="content-error" class="error"></div>
</div>

Expand Down
2 changes: 1 addition & 1 deletion app/Views/chat/svelteView.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>CodeIgniter Chat - Svelte Edition</title>
<meta name="csrf-token" content="<?= esc(csrf_hash()) ?>">
Expand Down
2 changes: 1 addition & 1 deletion app/Views/chat/vueView.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<title>CodeIgniter Chat - Vue.js Edition</title>
<meta name="csrf-token" content="<?= esc(csrf_hash()) ?>">
Expand Down
26 changes: 26 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,29 @@ composer test:integration:websocket
```

Keep actual browser and network-server end-to-end coverage separate from this suite.

## Browser end-to-end tests

Playwright covers the XML, JSON, HTML, Vue, and Svelte chat implementations in Chromium. Each scenario signs in through the real login page, posts a message, verifies that the message appears, checks the frontend's network payload when applicable, fails on browser errors, and runs axe against serious and critical accessibility violations.

Install the PHP and JavaScript dependencies, then install the Playwright browser once:

```shell
composer install
pnpm install
pnpm exec playwright install chromium
```

Run the complete browser suite with:

```shell
pnpm e2e
```

The harness starts the CodeIgniter HTTP server, Vite development server, and WebSocket server automatically. It migrates and seeds an isolated SQLite database and uses a dedicated cache directory, then removes those test artifacts and stops every process during teardown. The fixed `e2euser` credentials exist only in this isolated test database.

Use `pnpm e2e:ui` for Playwright's interactive runner. `pnpm e2e:codegen` opens the application for locator exploration after the stack is running. Traces, screenshots, and video are retained for failures in `test-results/`; the HTML report is written to `playwright-report/` in CI and uploaded when a pull-request run fails.

By default, the suite refuses to reuse processes already listening on its HTTP, Vite, or WebSocket ports so it cannot accidentally write through a non-test application stack. Set `PLAYWRIGHT_REUSE_SERVER=1` only when you intentionally started compatible HTTP and Vite servers with the same isolated E2E environment; WebSocket server reuse is never allowed.

Shared behavior belongs in `e2e/support/chat-scenarios.ts`; keep one small spec in `e2e/` for each frontend route. Pull requests run the suite through `.github/workflows/e2e.yml`.
9 changes: 9 additions & 0 deletions e2e/chat-html.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { chatScenario } from './support/chat-scenarios';

chatScenario({
name: 'HTML chat',
path: '/chat/html',
messageInput: '#content',
submitButton: 'input[type="submit"]',
authorInput: '#name',
});
10 changes: 10 additions & 0 deletions e2e/chat-json.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { chatScenario } from './support/chat-scenarios';

chatScenario({
name: 'JSON chat',
path: '/chat/json',
messageInput: '#content',
submitButton: 'input[type="submit"]',
authorInput: '#name',
network: { path: '/api/v1/messages', format: 'json' },
});
8 changes: 8 additions & 0 deletions e2e/chat-svelte.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { chatScenario } from './support/chat-scenarios';

chatScenario({
name: 'Svelte chat',
path: '/chat/svelte',
messageInput: '#message-input',
submitButton: 'button[type="submit"]',
});
8 changes: 8 additions & 0 deletions e2e/chat-vue.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { chatScenario } from './support/chat-scenarios';

chatScenario({
name: 'Vue chat',
path: '/chat/vue',
messageInput: '#message-input',
submitButton: 'button[type="submit"]',
});
9 changes: 9 additions & 0 deletions e2e/chat-xml.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { chatScenario } from './support/chat-scenarios';

chatScenario({
name: 'XML chat',
path: '/chat',
messageInput: '#content',
submitButton: 'input[type="submit"]',
network: { path: '/chat/backend', format: 'xml' },
});
Loading
Loading