ugly-app 0.1.804 → 0.1.805
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/package.json +3 -2
- package/src/cli/version.ts +1 -1
- package/templates/CLAUDE.md +16 -2
- package/templates/_features/tests/pages/UIComponentsPage.tsx +4 -4
- package/templates/tests/e2e/example-interactive.spec.ts +45 -0
- package/templates/tests/e2e/smoke.spec.ts +21 -3
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.805";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.805",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
6
6
|
"files": [
|
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
"release": "npm version patch --force && pnpm run build && npm publish && git push --follow-tags",
|
|
81
81
|
"release:quick": "SKIP_README=1 pnpm run release",
|
|
82
82
|
"test:e2e-pages": "playwright test --config playwright.e2e-pages.config.ts",
|
|
83
|
-
"test:full": "pnpm run test:parity && pnpm run test:e2e-pages"
|
|
83
|
+
"test:full": "pnpm run test:parity && pnpm run test:e2e-pages",
|
|
84
|
+
"test:template": "node scripts/test-template.mjs"
|
|
84
85
|
},
|
|
85
86
|
"dependencies": {
|
|
86
87
|
"@aws-sdk/client-s3": "^3.700.0",
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.805";
|
package/templates/CLAUDE.md
CHANGED
|
@@ -123,6 +123,20 @@ Every endpoint is accessible via both WebSocket (`socket.request(name, input)`)
|
|
|
123
123
|
versions which include accessibility attributes and element map support
|
|
124
124
|
- **Always** set `aria-label` on icon-only buttons and non-text interactive elements
|
|
125
125
|
|
|
126
|
+
## Verify UI changes by actually running them
|
|
127
|
+
|
|
128
|
+
A clean `tsc` and passing unit tests do NOT prove a page works — the #1 failure is
|
|
129
|
+
an app that compiles but crashes or does nothing when you open it. For any UI or
|
|
130
|
+
interactive work (a game, canvas, animation, form flow), run the Playwright e2e:
|
|
131
|
+
|
|
132
|
+
- `npm run test:e2e` (or `npx playwright test`) — runs `tests/e2e/`. The shipped
|
|
133
|
+
`smoke.spec.ts` loads the home route and FAILS on any console/page error, so a
|
|
134
|
+
runtime crash is caught immediately.
|
|
135
|
+
- For interactive pages, adapt `tests/e2e/example-interactive.spec.ts` (a skipped
|
|
136
|
+
template): it loads your page, drives real keyboard/pointer input for several
|
|
137
|
+
frames, and fails on any runtime error — the only way to catch gameplay/render
|
|
138
|
+
bugs a unit test on your engine logic will miss.
|
|
139
|
+
|
|
126
140
|
## UX inspection (`window.__uglyInspect()`)
|
|
127
141
|
|
|
128
142
|
Every page exposes a runtime inspection API installed by `bootstrapApp` at boot. It reports objective UX defects: layout shift (CLS), animation jank, overlapping interactive controls, safe-area violations, mobile keyboard coverage, popup mount cost, SPA route transitions. Observers run continuously from page load and keep a rolling 500-entry ring buffer per signal.
|
|
@@ -140,8 +154,8 @@ Every page exposes a runtime inspection API installed by `bootstrapApp` at boot.
|
|
|
140
154
|
- The expensive scans (overlap, safe-area) only run when `__uglyInspect()` is called — observers themselves are essentially free
|
|
141
155
|
|
|
142
156
|
## Feedback system
|
|
143
|
-
|
|
144
|
-
User feedback history: `GET /my_feedback` (requires auth cookie).
|
|
157
|
+
User feedback is collected by the Ugly Studio host (not by your app — do not add a
|
|
158
|
+
feedback UI). User feedback history: `GET /my_feedback` (requires auth cookie).
|
|
145
159
|
|
|
146
160
|
## Handling "needs images" feedback
|
|
147
161
|
When user feedback mentions missing or needed images:
|
|
@@ -957,10 +957,10 @@ function AdvancedTab(): React.ReactElement {
|
|
|
957
957
|
<Section title="Feedback" description="Feedback is a Ugly Studio–only feature">
|
|
958
958
|
<span style={{ opacity: 0.6, display: 'block', marginBottom: 12 }}>
|
|
959
959
|
<Text size="sm">
|
|
960
|
-
|
|
961
|
-
the framework registers a feedback handler over the UglyNative
|
|
962
|
-
Studio provides the feedback UI (capturing a screenshot + the
|
|
963
|
-
structured data), which is posted back to this app’s own server.
|
|
960
|
+
Your app renders no feedback UI of its own. When the app runs inside Ugly
|
|
961
|
+
Studio, the framework registers a feedback handler over the UglyNative
|
|
962
|
+
bridge and Studio provides the feedback UI (capturing a screenshot + the
|
|
963
|
+
page’s structured data), which is posted back to this app’s own server.
|
|
964
964
|
</Text>
|
|
965
965
|
</span>
|
|
966
966
|
</Section>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { expect, test } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* EXAMPLE (skipped) — how to test an interactive / canvas page (a game, a drawing
|
|
5
|
+
* tool, anything with a render loop + input). Delete this file or unskip + adapt it
|
|
6
|
+
* to YOUR page. A unit test on your engine logic is not enough: it won't catch the
|
|
7
|
+
* "builds fine but crashes / does nothing when you actually open and use it" bug.
|
|
8
|
+
* This pattern loads the real page, drives real input, and fails on any runtime error.
|
|
9
|
+
*
|
|
10
|
+
* Change `ROUTE`, the canvas/root selector, and the interactions to match your app.
|
|
11
|
+
*/
|
|
12
|
+
test.describe('interactive page (example — unskip + adapt)', () => {
|
|
13
|
+
const ROUTE = '/'; // e.g. '/' if your game IS the home page, or '/game'
|
|
14
|
+
|
|
15
|
+
test.skip('renders a canvas, takes input, and never throws', async ({ page }) => {
|
|
16
|
+
// 1. Fail on ANY uncaught error or console.error during the whole test.
|
|
17
|
+
const errors: string[] = [];
|
|
18
|
+
page.on('pageerror', (e) => errors.push(`pageerror: ${e.message}`));
|
|
19
|
+
page.on('console', (m) => { if (m.type() === 'error') errors.push(`console.error: ${m.text()}`); });
|
|
20
|
+
|
|
21
|
+
// 2. Load the page and confirm the canvas actually mounted.
|
|
22
|
+
await page.goto(ROUTE);
|
|
23
|
+
const canvas = page.locator('canvas').first();
|
|
24
|
+
await expect(canvas).toBeVisible();
|
|
25
|
+
|
|
26
|
+
// 3. Drive real input — keyboard, pointer/mouse, clicks. For a game, start it and
|
|
27
|
+
// play a few frames so the render loop + collision code actually execute.
|
|
28
|
+
await canvas.click(); // focus / start
|
|
29
|
+
await page.keyboard.press('Space'); // e.g. launch/serve
|
|
30
|
+
for (let i = 0; i < 8; i++) {
|
|
31
|
+
await page.keyboard.down('ArrowRight');
|
|
32
|
+
await page.waitForTimeout(60); // let requestAnimationFrame tick
|
|
33
|
+
await page.keyboard.up('ArrowRight');
|
|
34
|
+
}
|
|
35
|
+
// Pointer control (paddle-follows-mouse style):
|
|
36
|
+
const box = await canvas.boundingBox();
|
|
37
|
+
if (box) await page.mouse.move(box.x + box.width * 0.7, box.y + box.height * 0.9);
|
|
38
|
+
|
|
39
|
+
// 4. Assert something OBSERVABLE changed (score/lives/UI text), and no error fired.
|
|
40
|
+
// Replace with a real signal from your UI, e.g.:
|
|
41
|
+
// await expect(page.getByTestId('score')).not.toHaveText('0');
|
|
42
|
+
await page.waitForTimeout(200);
|
|
43
|
+
expect(errors, `runtime errors while playing:\n${errors.join('\n')}`).toEqual([]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { expect, test } from '@playwright/test';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// App-agnostic smoke test. Asserts the app boots and the home route renders WITHOUT
|
|
4
|
+
// asserting any specific content — apps are meant to replace the home page with their
|
|
5
|
+
// own UI, so this test must never pin scaffold-specific elements (a test that does
|
|
6
|
+
// would punish you for building your actual app). It also fails on any uncaught error
|
|
7
|
+
// thrown during load, which catches the most common "looks built but crashes when you
|
|
8
|
+
// open it" bug. Use this as the pattern for your own page tests: navigate, assert your
|
|
9
|
+
// UI rendered, and assert no console/page errors.
|
|
10
|
+
test('home page boots and renders without errors', async ({ page }) => {
|
|
11
|
+
const errors: string[] = [];
|
|
12
|
+
page.on('pageerror', (err) => errors.push(`pageerror: ${err.message}`));
|
|
13
|
+
page.on('console', (msg) => {
|
|
14
|
+
if (msg.type() === 'error') errors.push(`console.error: ${msg.text()}`);
|
|
15
|
+
});
|
|
16
|
+
|
|
4
17
|
await page.goto('/');
|
|
5
|
-
|
|
6
|
-
|
|
18
|
+
|
|
19
|
+
// The app mounts into #root (see client/index.html). Assert it rendered something.
|
|
20
|
+
const root = page.locator('#root');
|
|
21
|
+
await expect(root).toBeVisible();
|
|
22
|
+
await expect(root).not.toBeEmpty();
|
|
23
|
+
|
|
24
|
+
expect(errors, `App logged errors on load:\n${errors.join('\n')}`).toEqual([]);
|
|
7
25
|
});
|