diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bdfffd..2ab3cff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,12 @@ Versions follow [Semantic Versioning](https://semver.org/) — minor bump per su Writes target the row captured at `GET` time, mirroring how `grid-edit` already writes by rowid. This is the PR that promotes `LOOKUP` to the README command reference in full — both BROWSE and forms now declare, enforce, and render it end to end. +- **Demos adopt `LOOKUP`** (#61). `demos/overtime.prg` gains a `SCHEDULES` catalog table + (`SCHEDID`, `DESCR`) as the lookup source for `EMPLOYEES.SCHEDID` — Add Employee is now a + check-first, two-form flow where the schedule is picked from a dropdown showing the + description ("Standard 40h (08:00-16:30)") instead of typed from memory. `demos/crm.prg`'s + `DEALS.STAGE` is constrained to a literal `LOOKUP` list matching its own seeded vocabulary + exactly, exercising the other lookup kind in a real, working demo. ## [1.2.0] — 2026-07-09 — TIME columns, WEEK()/DATEADD(), BROWSE cell validation, Overtime demo diff --git a/CLAUDE.md b/CLAUDE.md index 3ebabc1..1a2ca58 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -378,7 +378,7 @@ npm run coverage # Vitest + v8 coverage report (reporting only, no thresh npx playwright test # E2E browser tests — requires dev server on :5173/:3000 ``` -Playwright suites (94 tests): `tests/assistant.spec.ts` (23 tests — sidebar, wizards, report designer, MODIFY STRUCTURE round-trip, `TIME(15)` column + REPLACE validation, `NUM(p,s)` wizard, Browse-action grid validation, program run, CSV/SORT/SUM-AVERAGE/REINDEX/PACK actions, demo launchers), `tests/integration.spec.ts` (20 tests — full REPL scenario), `tests/overtime.spec.ts` (9 tests — overtime.prg: menu, seeding, DATEADD week prep, TIME(15) grid rejection, recalculation, live balance, quarter-hour leave check, report, CSV), `tests/inventory.spec.ts` (8 tests — INVENTORY.prg menu + valuation/low-stock report/sort/CSV/JOIN), `tests/crm.spec.ts` (6 tests — CRM demo menu, pipeline summary, sort, report, CSV, JOIN), `tests/parity-commands.spec.ts` (6 tests — `?`/`??`, built-in functions, `WEEK()`, `DATEADD()`, `SUM`/`AVERAGE`, `SORT ON … TO`), `tests/multiarea.spec.ts` (4 tests — multi-work-area, relations, alias.field), `tests/demos.spec.ts` (4 tests — demo program + report seeding), `tests/grid-validation.spec.ts` (3 tests — BROWSE per-cell validation: TIME(15), NUM(p,s)/DATE, Esc abandons), `tests/schema-errors.spec.ts` (3 tests — malformed CREATE TABLE errors, NUM(p,s) column count, bare INPUT stores its value), `tests/copycsv.spec.ts` (2 tests — COPY TO download + APPEND FROM upload), `tests/splash.spec.ts` (2 tests — version banner + demo discoverability), `tests/join.spec.ts` (1 test — JOIN materialization), `tests/propagation.spec.ts` (1 test — live multiuser refresh), `tests/program-side-effects.spec.ts` (1 test — CSV/report side-effects fire from inside a program block). +Playwright suites (95 tests): `tests/assistant.spec.ts` (23 tests — sidebar, wizards, report designer, MODIFY STRUCTURE round-trip, `TIME(15)` column + REPLACE validation, `NUM(p,s)` wizard, Browse-action grid validation, program run, CSV/SORT/SUM-AVERAGE/REINDEX/PACK actions, demo launchers), `tests/integration.spec.ts` (20 tests — full REPL scenario), `tests/overtime.spec.ts` (10 tests — overtime.prg: menu, seeding, DATEADD week prep, TIME(15) grid rejection, recalculation, live balance, quarter-hour leave check, report, CSV, Add-Employee lookup dropdown), `tests/inventory.spec.ts` (8 tests — INVENTORY.prg menu + valuation/low-stock report/sort/CSV/JOIN), `tests/crm.spec.ts` (6 tests — CRM demo menu, pipeline summary, sort, report, CSV, JOIN), `tests/parity-commands.spec.ts` (6 tests — `?`/`??`, built-in functions, `WEEK()`, `DATEADD()`, `SUM`/`AVERAGE`, `SORT ON … TO`), `tests/multiarea.spec.ts` (4 tests — multi-work-area, relations, alias.field), `tests/demos.spec.ts` (4 tests — demo program + report seeding), `tests/grid-validation.spec.ts` (3 tests — BROWSE per-cell validation: TIME(15), NUM(p,s)/DATE, Esc abandons), `tests/schema-errors.spec.ts` (3 tests — malformed CREATE TABLE errors, NUM(p,s) column count, bare INPUT stores its value), `tests/copycsv.spec.ts` (2 tests — COPY TO download + APPEND FROM upload), `tests/splash.spec.ts` (2 tests — version banner + demo discoverability), `tests/join.spec.ts` (1 test — JOIN materialization), `tests/propagation.spec.ts` (1 test — live multiuser refresh), `tests/program-side-effects.spec.ts` (1 test — CSV/report side-effects fire from inside a program block). ## Test discipline diff --git a/demos/crm.prg b/demos/crm.prg index 449560e..75c8b05 100644 --- a/demos/crm.prg +++ b/demos/crm.prg @@ -45,7 +45,7 @@ IF RECCOUNT() == 0 SELECT DEAL DROP TABLE DEALS - CREATE TABLE DEALS (DEALID CHAR(6), COMPID CHAR(5), TITLE CHAR(40), STAGE CHAR(12), VALUE NUM(12,2), CLOSEMONTH NUM(6)) + CREATE TABLE DEALS (DEALID CHAR(6), COMPID CHAR(5), TITLE CHAR(40), STAGE CHAR(12) LOOKUP ("Lead","Qualified","Proposal","Won","Lost"), VALUE NUM(12,2), CLOSEMONTH NUM(6)) INDEX ON DEALID TO BYDEAL INDEX ON COMPID TO DEALCOMP APPEND RECORD diff --git a/demos/overtime.prg b/demos/overtime.prg index 237146b..c0808db 100644 --- a/demos/overtime.prg +++ b/demos/overtime.prg @@ -31,6 +31,10 @@ SELECT SCH USE DATABASE OVERTIME USE SCHEDULEDAYS +SELECT SCD +USE DATABASE OVERTIME +USE SCHEDULES + SELECT TS USE DATABASE OVERTIME USE TIMESHEET @@ -52,8 +56,17 @@ USE LEAVETAKEN * wipe TIMESHEET and WEEKSUMMARY — on every run until someone used it. SELECT EMP IF RECCOUNT() == 0 + SELECT SCD + DROP TABLE SCHEDULES + CREATE TABLE SCHEDULES (SCHEDID CHAR(4), DESCR CHAR(30)) + APPEND RECORD + REPLACE SCHEDID WITH "S001", DESCR WITH "Standard 40h (08:00-16:30)" + APPEND RECORD + REPLACE SCHEDID WITH "S002", DESCR WITH "Short 31.25h (09:00-16:00)" + + SELECT EMP DROP TABLE EMPLOYEES - CREATE TABLE EMPLOYEES (EMPID CHAR(4), NAME CHAR(30), SCHEDID CHAR(4)) + CREATE TABLE EMPLOYEES (EMPID CHAR(4), NAME CHAR(30), SCHEDID CHAR(4) LOOKUP SCHEDULES.SCHEDID DISPLAY DESCR) INDEX ON EMPID TO BYEMP APPEND RECORD REPLACE EMPID WITH "E001", NAME WITH "Ada Lovelace", SCHEDID WITH "S001" @@ -139,21 +152,24 @@ DO WHILE running CASE UPPER(TRIM(choice)) == "1" CLEAR @ 2, 5 SAY "--- ADD EMPLOYEE ---" - STORE SPACE(4) TO m_emp - STORE SPACE(30) TO m_name - STORE SPACE(4) TO m_sch - @ 4, 5 SAY "Employee ID (4): " GET m_emp - @ 5, 5 SAY "Name (30): " GET m_name - @ 6, 5 SAY "Schedule ID (4): " GET m_sch - READ SELECT EMP SET INDEX TO BYEMP + STORE SPACE(4) TO m_emp + @ 4, 5 SAY "Employee ID (4): " GET m_emp + READ SEEK TRIM(m_emp) IF FOUND() @ 8, 5 SAY "Employee already exists: " + TRIM(m_emp) ELSE + * Create in natural order: writing the key under an active index + * would move the record out from under the form. + SET INDEX TO APPEND RECORD - REPLACE EMPID WITH TRIM(m_emp), NAME WITH TRIM(m_name), SCHEDID WITH TRIM(m_sch) + REPLACE EMPID WITH TRIM(m_emp) + @ 5, 5 SAY "Name (30): " GET NAME + @ 6, 5 SAY "Schedule : " GET SCHEDID + READ + SET INDEX TO BYEMP @ 8, 5 SAY "Employee added: " + TRIM(m_emp) ENDIF INPUT "Press Enter to continue" TO pause diff --git a/tests/DemoSchemas.test.ts b/tests/DemoSchemas.test.ts index 4621c40..79d2e2c 100644 --- a/tests/DemoSchemas.test.ts +++ b/tests/DemoSchemas.test.ts @@ -25,6 +25,7 @@ const DEMO_SCHEMAS: Record = { // overtime.prg (#46) EMPLOYEES: ['EMPID', 'NAME', 'SCHEDID'], + SCHEDULES: ['SCHEDID', 'DESCR'], SCHEDULEDAYS: ['SCHEDID', 'DOW', 'TIMEIN', 'BSTART', 'BEND', 'TIMEOUT'], TIMESHEET: ['EMPID', 'WEEKDATE', 'DOW', 'WORKDATE', 'TIMEIN', 'BSTART', 'BEND', 'TIMEOUT', 'WORKEDHOURS'], WEEKSUMMARY: ['EMPID', 'WEEKDATE', 'WEEKNO', 'WORKEDHOURS', 'STANDARDHOURS', 'OVERTIME'], diff --git a/tests/overtime.spec.ts b/tests/overtime.spec.ts index aeeafe5..4ceb176 100644 --- a/tests/overtime.spec.ts +++ b/tests/overtime.spec.ts @@ -79,7 +79,7 @@ test.describe('Overtime demo', () => { await seedProgram(page); // Clean slate: the OVERTIME database persists server-side across suites. await cmd(page, 'USE DATABASE OVERTIME'); - for (const t of ['EMPLOYEES', 'SCHEDULEDAYS', 'TIMESHEET', 'WEEKSUMMARY', 'LEAVETAKEN']) { + for (const t of ['EMPLOYEES', 'SCHEDULES', 'SCHEDULEDAYS', 'TIMESHEET', 'WEEKSUMMARY', 'LEAVETAKEN']) { await cmd(page, `DROP TABLE ${t}`, 150); } await cmd(page, `DO ${PRG_NAME}`, 2000); @@ -100,6 +100,35 @@ test.describe('Overtime demo', () => { await ack(page); }); + test('Add Employee: the schedule is picked from a lookup dropdown, not typed', async ({ page }) => { + await menuChoice(page, '1'); // Add Employee → form 1 (id) + await expect(page.locator('#form-view')).toContainText('ADD EMPLOYEE', { timeout: 6000 }); + const idInput = page.locator('#form-view input.f-get').last(); + await idInput.fill('E003'); + await idInput.press('Enter'); + + // Form 2: NAME is a text field, SCHEDID is a