rewrite README; replace example e2e spec with a smoke test
README is now project-specific (features, stack, dev commands, docs). The Playwright spec checks home/inventories/wiki render and asserts no console errors.
This commit is contained in:
parent
6a07bd668d
commit
8bdf1c9be3
110
README.md
110
README.md
@ -1,72 +1,70 @@
|
|||||||
<picture>
|
# aex
|
||||||
<source srcset="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_Solid_White.svg" media="(prefers-color-scheme: dark)">
|
|
||||||
<img src="https://raw.githubusercontent.com/leptos-rs/leptos/main/docs/logos/Leptos_logo_RGB.svg" alt="Leptos Logo">
|
|
||||||
</picture>
|
|
||||||
|
|
||||||
# Leptos Starter Template
|
A D&D 5e character sheet and campaign dashboard for a text-based play-by-post
|
||||||
|
game. The UI is in Russian.
|
||||||
|
|
||||||
This is a template for use with the [Leptos](https://github.com/leptos-rs/leptos) web framework and the [cargo-leptos](https://github.com/akesson/cargo-leptos) tool.
|
## What works today
|
||||||
|
|
||||||
## Creating your template repo
|
- Campaign header: name, in-game date steppers, session counter
|
||||||
|
- Character sidebar: portrait, names, class, level, XP, hit points with
|
||||||
|
temp HP bar, hit dice, death saves, spell slots
|
||||||
|
- Inventories page (`/inv`): personal and party-common inventories,
|
||||||
|
currency per denomination, item rows with quantity and weight steppers,
|
||||||
|
move between inventories, remove
|
||||||
|
- Bundled 5e.tools item data (~5k items) with fuzzy search components
|
||||||
|
(search is not mounted on a page yet)
|
||||||
|
|
||||||
If you don't have `cargo-leptos` installed you can install it with
|
State is mock data: nothing is persisted, and several buttons and pages are
|
||||||
|
stubs. The phased plan to fix that is in [ROADMAP.md](./ROADMAP.md).
|
||||||
|
|
||||||
`cargo install cargo-leptos --locked`
|
## Tech stack
|
||||||
|
|
||||||
Then run
|
- Rust, edition 2024, nightly toolchain (pinned in `rust-toolchain.toml`)
|
||||||
|
- Leptos 0.8 SSR (actix-web) + client-side hydration
|
||||||
|
- `reactive_stores` for the reactive state model
|
||||||
|
- SCSS (`style/main.scss`), compiled by dart-sass
|
||||||
|
- Playwright for end-to-end tests
|
||||||
|
|
||||||
`cargo leptos new --git leptos-rs/start-actix`
|
## Development
|
||||||
|
|
||||||
to generate a new project template (you will be prompted to enter a project name).
|
Prerequisites: `cargo-leptos`, dart-sass, and the `wasm32-unknown-unknown`
|
||||||
|
target. The nightly toolchain installs itself via `rust-toolchain.toml`.
|
||||||
|
|
||||||
`cd {projectname}`
|
|
||||||
|
|
||||||
to go to your newly created project.
|
|
||||||
|
|
||||||
Of course, you should explore around the project structure, but the best place to start with your application code is in `src/app.rs`.
|
|
||||||
|
|
||||||
## Running your project
|
|
||||||
|
|
||||||
`cargo leptos watch`
|
|
||||||
By default, you can access your local project at `http://localhost:3000`
|
|
||||||
|
|
||||||
## Installing Additional Tools
|
|
||||||
|
|
||||||
By default, `cargo-leptos` uses `nightly` Rust, `cargo-generate`, and `sass`. If you run into any trouble, you may need to install one or more of these tools.
|
|
||||||
|
|
||||||
1. `rustup toolchain install nightly --allow-downgrade` - make sure you have Rust nightly
|
|
||||||
2. `rustup target add wasm32-unknown-unknown` - add the ability to compile Rust to WebAssembly
|
|
||||||
3. `cargo install cargo-generate` - install `cargo-generate` binary (should be installed automatically in future)
|
|
||||||
4. `npm install -g sass` - install `dart-sass` (should be optional in future)
|
|
||||||
|
|
||||||
## Executing a Server on a Remote Machine Without the Toolchain
|
|
||||||
After running a `cargo leptos build --release` the minimum files needed are:
|
|
||||||
|
|
||||||
1. The server binary located in `target/server/release`
|
|
||||||
2. The `site` directory and all files within located in `target/site`
|
|
||||||
|
|
||||||
Copy these files to your remote server. The directory structure should be:
|
|
||||||
```text
|
|
||||||
leptos_start
|
|
||||||
site/
|
|
||||||
```
|
|
||||||
Set the following environment variables (updating for your project as needed):
|
|
||||||
```sh
|
```sh
|
||||||
export LEPTOS_OUTPUT_NAME="leptos_start"
|
cargo install cargo-leptos
|
||||||
export LEPTOS_SITE_ROOT="site"
|
cargo leptos watch # dev server with hot reload
|
||||||
export LEPTOS_SITE_PKG_DIR="pkg"
|
# open http://127.0.0.1:3000
|
||||||
export LEPTOS_SITE_ADDR="127.0.0.1:3000"
|
|
||||||
export LEPTOS_RELOAD_PORT="3001"
|
|
||||||
```
|
```
|
||||||
Finally, run the server binary.
|
|
||||||
|
|
||||||
## Notes about CSR and Trunk:
|
Other commands:
|
||||||
Although it is not recommended, you can also run your project without server integration using the feature `csr` and `trunk serve`:
|
|
||||||
|
|
||||||
`trunk serve --open --features csr`
|
```sh
|
||||||
|
cargo leptos build --release # production build
|
||||||
|
cargo leptos serve # dev server without the watcher
|
||||||
|
cargo check --no-default-features --features ssr --bin aex
|
||||||
|
cargo check --no-default-features --features hydrate --lib
|
||||||
|
cargo check --target wasm32-unknown-unknown --no-default-features --features hydrate --lib
|
||||||
|
```
|
||||||
|
|
||||||
This may be useful for integrating external tools which require a static site, e.g. `tauri`.
|
## Tests
|
||||||
|
|
||||||
## Licensing
|
End-to-end smoke tests live in `end2end/` (Playwright). With the dev server
|
||||||
|
running on port 3000:
|
||||||
|
|
||||||
This template itself is released under the Unlicense. You should replace the LICENSE for your own application with an appropriate license if you plan to release it publicly.
|
```sh
|
||||||
|
cd end2end
|
||||||
|
npm install
|
||||||
|
npx playwright test
|
||||||
|
```
|
||||||
|
|
||||||
|
or let cargo-leptos manage the server: `cargo leptos end2end`.
|
||||||
|
|
||||||
|
## Project docs
|
||||||
|
|
||||||
|
- [ARCHITECTURE.md](./ARCHITECTURE.md) — how the code is organized
|
||||||
|
- [ROADMAP.md](./ROADMAP.md) — the phased plan to MVP
|
||||||
|
- [MODERNIZE.md](./MODERNIZE.md) — dependency and idiom cleanup list
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Public domain (Unlicense), see [LICENSE](./LICENSE).
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { test, expect } from "@playwright/test";
|
|
||||||
|
|
||||||
test("homepage has title and links to intro page", async ({ page }) => {
|
|
||||||
await page.goto("http://localhost:3000/");
|
|
||||||
|
|
||||||
await expect(page).toHaveTitle("Welcome to Leptos");
|
|
||||||
|
|
||||||
await expect(page.locator("h1")).toHaveText("Welcome to Leptos!");
|
|
||||||
});
|
|
||||||
56
end2end/tests/smoke.spec.ts
Normal file
56
end2end/tests/smoke.spec.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { test, expect, type Page } from "@playwright/test";
|
||||||
|
|
||||||
|
const BASE = "http://localhost:3000";
|
||||||
|
|
||||||
|
// Collects browser console errors and page errors for the duration of a test.
|
||||||
|
function trackErrors(page: Page): string[] {
|
||||||
|
const errors: string[] = [];
|
||||||
|
page.on("console", (msg) => {
|
||||||
|
if (msg.type() === "error") {
|
||||||
|
errors.push(msg.text());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
page.on("pageerror", (err) => {
|
||||||
|
errors.push(String(err));
|
||||||
|
});
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
test("home page: campaign name, version, nav", async ({ page }) => {
|
||||||
|
const errors = trackErrors(page);
|
||||||
|
await page.goto(`${BASE}/`);
|
||||||
|
|
||||||
|
await expect(page.locator("#campaign-name")).toHaveValue("В Поисках Дмитрия Шардалина");
|
||||||
|
await expect(page.locator(".version")).toContainText("aex");
|
||||||
|
await expect(page.locator(".main-nav a")).toHaveCount(3);
|
||||||
|
await expect(page.locator(".main-nav")).toContainText("Персонаж");
|
||||||
|
await expect(page.locator(".main-nav")).toContainText("Инвентарь");
|
||||||
|
await expect(page.locator(".main-nav")).toContainText("Информация");
|
||||||
|
|
||||||
|
expect(errors).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("inventories page: personal and common sections", async ({ page }) => {
|
||||||
|
const errors = trackErrors(page);
|
||||||
|
await page.goto(`${BASE}/inv`);
|
||||||
|
|
||||||
|
await expect(page.locator(".inventory-personal")).toHaveCount(1);
|
||||||
|
await expect(page.locator(".inventory-common")).toHaveCount(1);
|
||||||
|
// each section has one balance block with five currency inputs
|
||||||
|
await expect(page.locator(".inventory-personal .balance .numput")).toHaveCount(5);
|
||||||
|
await expect(page.locator(".inventory-common .balance .numput")).toHaveCount(5);
|
||||||
|
// and at least one item row each
|
||||||
|
await expect(page.locator(".inventory-personal .item").first()).toBeVisible();
|
||||||
|
await expect(page.locator(".inventory-common .item").first()).toBeVisible();
|
||||||
|
|
||||||
|
expect(errors).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("wiki page: stub renders", async ({ page }) => {
|
||||||
|
const errors = trackErrors(page);
|
||||||
|
await page.goto(`${BASE}/wiki`);
|
||||||
|
|
||||||
|
await expect(page.locator("body")).toContainText("books");
|
||||||
|
|
||||||
|
expect(errors).toEqual([]);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user