mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
Merge pull request #1077 from Demonthos/end-to-end-testing
create end to end tests using playwright
This commit is contained in:
commit
86402d3e23
18 changed files with 736 additions and 2 deletions
44
.github/workflows/playwright.yml
vendored
Normal file
44
.github/workflows/playwright.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
name: Playwright Tests
|
||||
on:
|
||||
push:
|
||||
branches: [ main, master ]
|
||||
pull_request:
|
||||
branches: [ main, master ]
|
||||
jobs:
|
||||
test:
|
||||
if: github.event.pull_request.draft == false
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Playwright
|
||||
run: npm install -D @playwright/test
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Install WASM toolchain
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
- name: Install Dioxus CLI
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: --git https://github.com/DioxusLabs/cli
|
||||
- name: Run Playwright tests
|
||||
run: npx playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
|||
/target
|
||||
/playwrite-tests/web/dist
|
||||
/playwrite-tests/fullstack/dist
|
||||
/dist
|
||||
Cargo.lock
|
||||
.DS_Store
|
||||
|
@ -11,4 +13,8 @@ Cargo.lock
|
|||
tarpaulin-report.html
|
||||
|
||||
# Jetbrain
|
||||
.idea/
|
||||
.idea/
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
|
|
|
@ -34,6 +34,10 @@ members = [
|
|||
# Full project examples
|
||||
"examples/tailwind",
|
||||
"examples/PWA-example",
|
||||
# Playwrite tests
|
||||
"playwrite-tests/liveview",
|
||||
"playwrite-tests/web",
|
||||
"playwrite-tests/fullstack",
|
||||
]
|
||||
|
||||
# dependencies that are shared across packages
|
||||
|
|
|
@ -41,6 +41,12 @@ cargo check --workspace --examples --tests
|
|||
cargo clippy --workspace --examples --tests -- -D warnings
|
||||
```
|
||||
|
||||
- Browser tests are automated with [Playwright](https://playwright.dev/docs/intro#installing-playwright)
|
||||
|
||||
```sh
|
||||
npx playwright test
|
||||
```
|
||||
|
||||
- Crates that use unsafe are checked for undefined behavior with [MIRI](https://github.com/rust-lang/miri). MIRI can be helpful to debug what unsafe code is causing issues. Only code that does not interact with system calls can be checked with MIRI. Currently, this is used for the two MIRI tests in `dioxus-core` and `dioxus-native-core`.
|
||||
|
||||
```sh
|
||||
|
|
67
package-lock.json
generated
Normal file
67
package-lock.json
generated
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "dioxus",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dioxus",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.34.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.34.3",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.34.3.tgz",
|
||||
"integrity": "sha512-zPLef6w9P6T/iT6XDYG3mvGOqOyb6eHaV9XtkunYs0+OzxBtrPAAaHotc0X+PJ00WPPnLfFBTl7mf45Mn8DBmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"playwright-core": "1.34.3"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz",
|
||||
"integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.34.3",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.34.3.tgz",
|
||||
"integrity": "sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
package.json
Normal file
17
package.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "dioxus",
|
||||
"version": "1.0.0",
|
||||
"description": "<p align=\"center\"> <img src=\"./notes/header.svg\"> </p>",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"doc": "docs",
|
||||
"example": "examples"
|
||||
},
|
||||
"scripts": {},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.34.3"
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ fn app(cx: Scope<AppProps>) -> Element {
|
|||
}
|
||||
}
|
||||
},
|
||||
"Run a server function! testing1234"
|
||||
"Run a server function!"
|
||||
}
|
||||
"Server said: {text}"
|
||||
})
|
||||
|
|
99
playwright.config.js
Normal file
99
playwright.config.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
// @ts-check
|
||||
const { defineConfig, devices } = require('@playwright/test');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// require('dotenv').config();
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
module.exports = defineConfig({
|
||||
testDir: './playwrite-tests',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* 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 on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* 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://127.0.0.1:3000',
|
||||
|
||||
/* 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'] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: [
|
||||
{
|
||||
command: 'cargo run --package dioxus-playwrite-liveview-test --bin dioxus-playwrite-liveview-test',
|
||||
port: 3030,
|
||||
timeout: 10 * 60 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: "pipe",
|
||||
},
|
||||
{
|
||||
cwd: path.join(process.cwd(), 'playwrite-tests', 'web'),
|
||||
command: 'dioxus serve',
|
||||
port: 8080,
|
||||
timeout: 10 * 60 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: "pipe",
|
||||
},
|
||||
{
|
||||
cwd: path.join(process.cwd(), 'playwrite-tests', 'fullstack'),
|
||||
command: 'dioxus build --features web\ncargo run --release --features ssr --no-default-features',
|
||||
port: 3333,
|
||||
timeout: 10 * 60 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
stdout: "pipe",
|
||||
}
|
||||
],
|
||||
});
|
32
playwrite-tests/fullstack.spec.js
Normal file
32
playwrite-tests/fullstack.spec.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
// @ts-check
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test('button click', async ({ page }) => {
|
||||
await page.goto('http://localhost:3333');
|
||||
|
||||
// Expect the page to contain the counter text.
|
||||
const main = page.locator('#main');
|
||||
await expect(main).toContainText('hello axum! 12345');
|
||||
|
||||
// Click the increment button.
|
||||
let button = await page.locator('button.increment-button');
|
||||
await button.click();
|
||||
|
||||
// Expect the page to contain the updated counter text.
|
||||
await expect(main).toContainText('hello axum! 12346');
|
||||
});
|
||||
|
||||
test('fullstack communication', async ({ page }) => {
|
||||
await page.goto('http://localhost:3333');
|
||||
|
||||
// Expect the page to contain the counter text.
|
||||
const main = page.locator('#main');
|
||||
await expect(main).toContainText('Server said: ...');
|
||||
|
||||
// Click the increment button.
|
||||
let button = await page.locator('button.server-button');
|
||||
await button.click();
|
||||
|
||||
// Expect the page to contain the updated counter text.
|
||||
await expect(main).toContainText('Server said: Hello from the server!');
|
||||
});
|
2
playwrite-tests/fullstack/.gitignore
vendored
Normal file
2
playwrite-tests/fullstack/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
dist
|
||||
target
|
21
playwrite-tests/fullstack/Cargo.toml
Normal file
21
playwrite-tests/fullstack/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "dioxus-playwrite-fullstack-test"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-web = { path = "../../packages/web", features=["hydrate"], optional = true }
|
||||
dioxus = { path = "../../packages/dioxus" }
|
||||
dioxus-fullstack = { path = "../../packages/fullstack" }
|
||||
axum = { version = "0.6.12", optional = true }
|
||||
tokio = { version = "1.27.0", features = ["full"], optional = true }
|
||||
serde = "1.0.159"
|
||||
execute = "0.2.12"
|
||||
|
||||
[features]
|
||||
default = ["web"]
|
||||
ssr = ["axum", "tokio", "dioxus-fullstack/axum"]
|
||||
web = ["dioxus-web"]
|
104
playwrite-tests/fullstack/src/main.rs
Normal file
104
playwrite-tests/fullstack/src/main.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
// This test is used by playwrite configured in the root of the repo
|
||||
// Tests:
|
||||
// - Server functions
|
||||
// - SSR
|
||||
// - Hydration
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_fullstack::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "web")]
|
||||
dioxus_web::launch_with_props(
|
||||
app,
|
||||
get_root_props_from_document().unwrap_or_default(),
|
||||
dioxus_web::Config::new().hydrate(true),
|
||||
);
|
||||
#[cfg(feature = "ssr")]
|
||||
{
|
||||
// Start hot reloading
|
||||
hot_reload_init!(dioxus_hot_reload::Config::new().with_rebuild_callback(|| {
|
||||
execute::shell("dioxus build --features web")
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
execute::shell("cargo run --features ssr --no-default-features")
|
||||
.spawn()
|
||||
.unwrap();
|
||||
true
|
||||
}));
|
||||
|
||||
PostServerData::register().unwrap();
|
||||
GetServerData::register().unwrap();
|
||||
tokio::runtime::Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(async move {
|
||||
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3333));
|
||||
axum::Server::bind(&addr)
|
||||
.serve(
|
||||
axum::Router::new()
|
||||
.serve_dioxus_application(
|
||||
"",
|
||||
ServeConfigBuilder::new(app, AppProps { count: 12345 }).build(),
|
||||
)
|
||||
.into_make_service(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Props, PartialEq, Debug, Default, Serialize, Deserialize, Clone)]
|
||||
struct AppProps {
|
||||
count: i32,
|
||||
}
|
||||
|
||||
fn app(cx: Scope<AppProps>) -> Element {
|
||||
let mut count = use_state(cx, || cx.props.count);
|
||||
let text = use_state(cx, || "...".to_string());
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "hello axum! {count}" }
|
||||
button {
|
||||
class: "increment-button",
|
||||
onclick: move |_| count += 1,
|
||||
"Increment"
|
||||
}
|
||||
button {
|
||||
class: "server-button",
|
||||
onclick: move |_| {
|
||||
to_owned![text];
|
||||
let sc = cx.sc();
|
||||
async move {
|
||||
if let Ok(data) = get_server_data().await {
|
||||
println!("Client received: {}", data);
|
||||
text.set(data.clone());
|
||||
post_server_data(sc, data).await.unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
"Run a server function!"
|
||||
}
|
||||
"Server said: {text}"
|
||||
})
|
||||
}
|
||||
|
||||
#[server(PostServerData)]
|
||||
async fn post_server_data(cx: DioxusServerContext, data: String) -> Result<(), ServerFnError> {
|
||||
// The server context contains information about the current request and allows you to modify the response.
|
||||
cx.response_headers_mut()
|
||||
.insert("Set-Cookie", "foo=bar".parse().unwrap());
|
||||
println!("Server received: {}", data);
|
||||
println!("Request parts are {:?}", cx.request_parts());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[server(GetServerData)]
|
||||
async fn get_server_data() -> Result<String, ServerFnError> {
|
||||
Ok("Hello from the server!".to_string())
|
||||
}
|
72
playwrite-tests/liveview.spec.js
Normal file
72
playwrite-tests/liveview.spec.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
// @ts-check
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test('button click', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the counter text.
|
||||
const main = page.locator('#main');
|
||||
await expect(main).toContainText('hello axum! 0');
|
||||
|
||||
// Click the increment button.
|
||||
await page.getByRole('button', { name: 'Increment' }).click();
|
||||
|
||||
// Expect the page to contain the updated counter text.
|
||||
await expect(main).toContainText('hello axum! 1');
|
||||
});
|
||||
|
||||
test('svg', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the svg.
|
||||
const svg = page.locator('svg');
|
||||
|
||||
// Expect the svg to contain the circle.
|
||||
const circle = svg.locator('circle');
|
||||
await expect(circle).toHaveAttribute('cx', '50');
|
||||
await expect(circle).toHaveAttribute('cy', '50');
|
||||
await expect(circle).toHaveAttribute('r', '40');
|
||||
await expect(circle).toHaveAttribute('stroke', 'green');
|
||||
await expect(circle).toHaveAttribute('fill', 'yellow');
|
||||
});
|
||||
|
||||
test('raw attribute', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the div with the raw attribute.
|
||||
const div = page.locator('div.raw-attribute-div');
|
||||
await expect(div).toHaveAttribute('raw-attribute', 'raw-attribute-value');
|
||||
});
|
||||
|
||||
test('hidden attribute', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the div with the hidden attribute.
|
||||
const div = page.locator('div.hidden-attribute-div');
|
||||
await expect(div).toHaveAttribute('hidden', 'true');
|
||||
});
|
||||
|
||||
test('dangerous inner html', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the div with the dangerous inner html.
|
||||
const div = page.locator('div.dangerous-inner-html-div');
|
||||
await expect(div).toContainText('hello dangerous inner html');
|
||||
});
|
||||
|
||||
test('input value', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the input with the value.
|
||||
const input = page.locator('input');
|
||||
await expect(input).toHaveValue('hello input');
|
||||
});
|
||||
|
||||
test('style', async ({ page }) => {
|
||||
await page.goto('http://127.0.0.1:3030');
|
||||
|
||||
// Expect the page to contain the div with the style.
|
||||
const div = page.locator('div.style-div');
|
||||
await expect(div).toHaveText('colored text');
|
||||
await expect(div).toHaveCSS('color', 'rgb(255, 0, 0)');
|
||||
});
|
13
playwrite-tests/liveview/Cargo.toml
Normal file
13
playwrite-tests/liveview/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "dioxus-playwrite-liveview-test"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
description = "Playwrite test for Dioxus Liveview"
|
||||
license = "MIT/Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
dioxus = { path = "../../packages/dioxus" }
|
||||
dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
|
||||
tokio = { version = "1.19.2", features = ["full"] }
|
||||
axum = { version = "0.6.1", features = ["ws"] }
|
78
playwrite-tests/liveview/src/main.rs
Normal file
78
playwrite-tests/liveview/src/main.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
// This test is used by playwrite configured in the root of the repo
|
||||
|
||||
use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router};
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut num = use_state(cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"hello axum! {num}"
|
||||
button { onclick: move |_| num += 1, "Increment" }
|
||||
}
|
||||
svg {
|
||||
circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" }
|
||||
}
|
||||
div {
|
||||
class: "raw-attribute-div",
|
||||
"raw-attribute": "raw-attribute-value",
|
||||
}
|
||||
div {
|
||||
class: "hidden-attribute-div",
|
||||
hidden: true,
|
||||
}
|
||||
div {
|
||||
class: "dangerous-inner-html-div",
|
||||
dangerous_inner_html: "<p>hello dangerous inner html</p>",
|
||||
}
|
||||
input {
|
||||
value: "hello input",
|
||||
}
|
||||
div {
|
||||
class: "style-div",
|
||||
color: "red",
|
||||
"colored text"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
|
||||
|
||||
let view = dioxus_liveview::LiveViewPool::new();
|
||||
|
||||
let app = Router::new()
|
||||
.route(
|
||||
"/",
|
||||
get(move || async move {
|
||||
Html(format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head> <title>Dioxus LiveView with axum</title> </head>
|
||||
<body> <div id="main"></div> </body>
|
||||
{glue}
|
||||
</html>
|
||||
"#,
|
||||
glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws"))
|
||||
))
|
||||
}),
|
||||
)
|
||||
.route(
|
||||
"/ws",
|
||||
get(move |ws: WebSocketUpgrade| async move {
|
||||
ws.on_upgrade(move |socket| async move {
|
||||
_ = view.launch(dioxus_liveview::axum_socket(socket), app).await;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
println!("Listening on http://{addr}");
|
||||
|
||||
axum::Server::bind(&addr.to_string().parse().unwrap())
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
94
playwrite-tests/web.spec.js
Normal file
94
playwrite-tests/web.spec.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
// @ts-check
|
||||
const { test, expect, defineConfig } = require('@playwright/test');
|
||||
|
||||
test('button click', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the counter text.
|
||||
const main = page.locator('#main');
|
||||
await expect(main).toContainText('hello axum! 0');
|
||||
|
||||
// Click the increment button.
|
||||
let button = await page.locator('button.increment-button');
|
||||
await button.click();
|
||||
|
||||
// Expect the page to contain the updated counter text.
|
||||
await expect(main).toContainText('hello axum! 1');
|
||||
});
|
||||
|
||||
test('svg', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the svg.
|
||||
const svg = page.locator('svg');
|
||||
|
||||
// Expect the svg to contain the circle.
|
||||
const circle = svg.locator('circle');
|
||||
await expect(circle).toHaveAttribute('cx', '50');
|
||||
await expect(circle).toHaveAttribute('cy', '50');
|
||||
await expect(circle).toHaveAttribute('r', '40');
|
||||
await expect(circle).toHaveAttribute('stroke', 'green');
|
||||
await expect(circle).toHaveAttribute('fill', 'yellow');
|
||||
});
|
||||
|
||||
test('raw attribute', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the div with the raw attribute.
|
||||
const div = page.locator('div.raw-attribute-div');
|
||||
await expect(div).toHaveAttribute('raw-attribute', 'raw-attribute-value');
|
||||
});
|
||||
|
||||
test('hidden attribute', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the div with the hidden attribute.
|
||||
const div = page.locator('div.hidden-attribute-div');
|
||||
await expect(div).toHaveAttribute('hidden', 'true');
|
||||
});
|
||||
|
||||
test('dangerous inner html', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the div with the dangerous inner html.
|
||||
const div = page.locator('div.dangerous-inner-html-div');
|
||||
await expect(div).toContainText('hello dangerous inner html');
|
||||
});
|
||||
|
||||
test('input value', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the input with the value.
|
||||
const input = page.locator('input');
|
||||
await expect(input).toHaveValue('hello input');
|
||||
});
|
||||
|
||||
test('style', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the div with the style.
|
||||
const div = page.locator('div.style-div');
|
||||
await expect(div).toHaveText('colored text');
|
||||
await expect(div).toHaveCSS('color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
|
||||
test('eval', async ({ page }) => {
|
||||
await page.goto('http://localhost:8080');
|
||||
|
||||
// Expect the page to contain the div with the eval and have no text.
|
||||
const div = page.locator('div.eval-result');
|
||||
await expect(div).toHaveText('');
|
||||
|
||||
// Click the button to run the eval.
|
||||
let button = await page.locator('button.eval-button');
|
||||
await button.click();
|
||||
|
||||
// Check that the title changed.
|
||||
await expect(page).toHaveTitle('Hello from Dioxus Eval!');
|
||||
|
||||
// Check that the div has the eval value.
|
||||
await expect(div).toHaveText('returned eval value');
|
||||
|
||||
});
|
||||
|
||||
// Shutdown the li
|
12
playwrite-tests/web/Cargo.toml
Normal file
12
playwrite-tests/web/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "dioxus-playwrite-web-test"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
description = "Playwrite test for Dioxus Web"
|
||||
license = "MIT/Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
dioxus = { path = "../../packages/dioxus" }
|
||||
dioxus-web = { path = "../../packages/web" }
|
||||
serde_json = "1.0.96"
|
63
playwrite-tests/web/src/main.rs
Normal file
63
playwrite-tests/web/src/main.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
// This test is used by playwrite configured in the root of the repo
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_web::use_eval;
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut num = use_state(cx, || 0);
|
||||
let eval = use_eval(cx);
|
||||
let eval_result = use_state(cx, String::new);
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
"hello axum! {num}"
|
||||
button {
|
||||
class: "increment-button",
|
||||
onclick: move |_| num += 1, "Increment"
|
||||
}
|
||||
}
|
||||
svg {
|
||||
circle { cx: 50, cy: 50, r: 40, stroke: "green", fill: "yellow" }
|
||||
}
|
||||
div {
|
||||
class: "raw-attribute-div",
|
||||
"raw-attribute": "raw-attribute-value",
|
||||
}
|
||||
div {
|
||||
class: "hidden-attribute-div",
|
||||
hidden: true,
|
||||
}
|
||||
div {
|
||||
class: "dangerous-inner-html-div",
|
||||
dangerous_inner_html: "<p>hello dangerous inner html</p>",
|
||||
}
|
||||
input {
|
||||
value: "hello input",
|
||||
}
|
||||
div {
|
||||
class: "style-div",
|
||||
color: "red",
|
||||
"colored text"
|
||||
}
|
||||
button {
|
||||
class: "eval-button",
|
||||
onclick: move |_| {
|
||||
// Set the window title
|
||||
let result = eval(r#"window.document.title = 'Hello from Dioxus Eval!';
|
||||
return "returned eval value";"#.to_string());
|
||||
if let Ok(serde_json::Value::String(string)) = result.get() {
|
||||
eval_result.set(string);
|
||||
}
|
||||
},
|
||||
"Eval"
|
||||
}
|
||||
div {
|
||||
class: "eval-result",
|
||||
"{eval_result}"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
dioxus_web::launch(app);
|
||||
}
|
Loading…
Reference in a new issue