Compare commits

...

2 Commits

Author SHA1 Message Date
Felipe Cardoso
07309013d7 chore(frontend): update scripts and docs to use bun run test for consistency
- Replaced `bun test` with `bun run test` in all documentation and scripts for uniformity.
- Removed outdated `glob` override in package configurations.
2026-03-01 18:44:48 +01:00
Felipe Cardoso
846fc31190 feat(api): enhance KeyMap and FieldsConfig handling for improved flexibility
- Added support for unmapped fields in `KeyMap` definitions and parsing.
- Updated `buildKeyMap` to allow aliasing keys without transport layer mappings.
- Improved parameter assignment logic to handle optional `in` mappings.
- Enhanced handling of `allowExtra` fields for more concise and robust configurations.
2026-03-01 18:01:34 +01:00
13 changed files with 56 additions and 27 deletions

View File

@@ -165,7 +165,7 @@ Permission dependencies in `api/dependencies/permissions.py`:
**Frontend Unit Tests (Jest):** **Frontend Unit Tests (Jest):**
- 97% coverage - 97% coverage
- Component, hook, and utility testing - Component, hook, and utility testing
- Run: `bun test` - Run: `bun run test`
- Coverage: `bun run test:coverage` - Coverage: `bun run test:coverage`
**Frontend E2E Tests (Playwright):** **Frontend E2E Tests (Playwright):**

View File

@@ -51,7 +51,7 @@ EOF
**Testing Commands:** **Testing Commands:**
- Backend unit/integration: `IS_TEST=True uv run pytest` (always prefix with `IS_TEST=True`) - Backend unit/integration: `IS_TEST=True uv run pytest` (always prefix with `IS_TEST=True`)
- Backend E2E (requires Docker): `make test-e2e` - Backend E2E (requires Docker): `make test-e2e`
- Frontend unit: `bun test` - Frontend unit: `bun run test`
- Frontend E2E: `bun run test:e2e` - Frontend E2E: `bun run test:e2e`
- Use `make test` or `make test-cov` in backend for convenience - Use `make test` or `make test-cov` in backend for convenience

View File

@@ -131,7 +131,7 @@ cp .env.local.example .env.local
bun run generate:api bun run generate:api
# Run tests # Run tests
bun test bun run test
bun run test:e2e:ui bun run test:e2e:ui
# Start dev server # Start dev server

View File

@@ -390,7 +390,7 @@ open htmlcov/index.html
cd frontend cd frontend
# Run unit tests # Run unit tests
bun test bun run test
# Run with coverage # Run with coverage
bun run test:coverage bun run test:coverage

View File

@@ -86,7 +86,7 @@ bun run type-check # TypeScript type checking
bun run validate # Run all checks (lint + format + type-check) bun run validate # Run all checks (lint + format + type-check)
# Testing # Testing
bun test # Run unit tests bun run test # Run unit tests
bun run test:watch # Watch mode bun run test:watch # Watch mode
bun run test:coverage # Coverage report bun run test:coverage # Coverage report
bun run test:e2e # Run E2E tests bun run test:e2e # Run E2E tests
@@ -184,7 +184,7 @@ See [docs/I18N.md](./docs/I18N.md) for complete guide.
```bash ```bash
# Run all tests # Run all tests
bun test bun run test
# Watch mode # Watch mode
bun run test:watch bun run test:watch

View File

@@ -78,9 +78,6 @@
}, },
}, },
}, },
"overrides": {
"glob": "^10.4.1",
},
"packages": { "packages": {
"@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="], "@adobe/css-tools": ["@adobe/css-tools@4.4.4", "", {}, "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg=="],
@@ -1316,6 +1313,8 @@
"framer-motion": ["framer-motion@12.34.3", "", { "dependencies": { "motion-dom": "^12.34.3", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q=="], "framer-motion": ["framer-motion@12.34.3", "", { "dependencies": { "motion-dom": "^12.34.3", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
@@ -1442,6 +1441,10 @@
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
"internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
@@ -1930,6 +1933,8 @@
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
@@ -2604,6 +2609,8 @@
"string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
"use-intl/@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.0", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg=="], "use-intl/@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.0", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg=="],
"use-intl/intl-messageformat": ["intl-messageformat@11.1.2", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.1", "@formatjs/fast-memoize": "3.1.0", "@formatjs/icu-messageformat-parser": "3.5.1", "tslib": "^2.8.1" } }, "sha512-ucSrQmZGAxfiBHfBRXW/k7UC8MaGFlEj4Ry1tKiDcmgwQm1y3EDl40u+4VNHYomxJQMJi9NEI3riDRlth96jKg=="], "use-intl/intl-messageformat": ["intl-messageformat@11.1.2", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.1", "@formatjs/fast-memoize": "3.1.0", "@formatjs/icu-messageformat-parser": "3.5.1", "tslib": "^2.8.1" } }, "sha512-ucSrQmZGAxfiBHfBRXW/k7UC8MaGFlEj4Ry1tKiDcmgwQm1y3EDl40u+4VNHYomxJQMJi9NEI3riDRlth96jKg=="],

View File

@@ -1538,7 +1538,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: bun install --frozen-lockfile run: bun install --frozen-lockfile
- name: Run tests - name: Run tests
run: bun test run: bun run test
- name: Run linter - name: Run linter
run: bun run lint run: bun run lint
- name: Type check - name: Type check

View File

@@ -914,7 +914,7 @@ bun run type-check
bun run lint bun run lint
# Tests # Tests
bun test bun run test
# Build check # Build check
bun run build bun run build

View File

@@ -233,7 +233,7 @@ MSW never initializes during Jest tests:
- 97%+ coverage maintained - 97%+ coverage maintained
```bash ```bash
bun test # MSW will NOT interfere bun run test # MSW will NOT interfere
``` ```
### E2E Tests (Playwright) ### E2E Tests (Playwright)

View File

@@ -99,6 +99,5 @@
] ]
}, },
"overrides": { "overrides": {
"glob": "^10.4.1"
} }
} }

View File

@@ -152,7 +152,7 @@ type BuildUrlFn = <
url: string; url: string;
}, },
>( >(
options: Pick<TData, 'url'> & Options<TData>, options: TData & Options<TData>,
) => string; ) => string;
export type Client = CoreClient< export type Client = CoreClient<
@@ -195,7 +195,7 @@ export type Options<
RequestOptions<TResponse, ThrowOnError>, RequestOptions<TResponse, ThrowOnError>,
'body' | 'path' | 'query' | 'url' 'body' | 'path' | 'query' | 'url'
> & > &
Omit<TData, 'url'>; ([TData] extends [never] ? unknown : Omit<TData, 'url'>);
export type OptionsLegacyParser< export type OptionsLegacyParser<
TData = unknown, TData = unknown,

View File

@@ -23,6 +23,17 @@ export type Field =
*/ */
key?: string; key?: string;
map?: string; map?: string;
}
| {
/**
* Field name. This is the name we want the user to see and use.
*/
key: string;
/**
* Field mapped name. This is the name we want to use in the request.
* If `in` is omitted, `map` aliases `key` to the transport layer.
*/
map: Slot;
}; };
export interface Fields { export interface Fields {
@@ -42,10 +53,14 @@ const extraPrefixes = Object.entries(extraPrefixesMap);
type KeyMap = Map< type KeyMap = Map<
string, string,
{ | {
in: Slot; in: Slot;
map?: string; map?: string;
} }
| {
in?: never;
map: Slot;
}
>; >;
const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
@@ -61,6 +76,10 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
map: config.map, map: config.map,
}); });
} }
} else if ('key' in config) {
map.set(config.key, {
map: config.map,
});
} else if (config.args) { } else if (config.args) {
buildKeyMap(config.args, map); buildKeyMap(config.args, map);
} }
@@ -112,7 +131,9 @@ export const buildClientParams = (
if (config.key) { if (config.key) {
const field = map.get(config.key)!; const field = map.get(config.key)!;
const name = field.map || config.key; const name = field.map || config.key;
(params[field.in] as Record<string, unknown>)[name] = arg; if (field.in) {
(params[field.in] as Record<string, unknown>)[name] = arg;
}
} else { } else {
params.body = arg; params.body = arg;
} }
@@ -121,8 +142,12 @@ export const buildClientParams = (
const field = map.get(key); const field = map.get(key);
if (field) { if (field) {
const name = field.map || key; if (field.in) {
(params[field.in] as Record<string, unknown>)[name] = value; const name = field.map || key;
(params[field.in] as Record<string, unknown>)[name] = value;
} else {
params[field.map] = value;
}
} else { } else {
const extra = extraPrefixes.find(([prefix]) => const extra = extraPrefixes.find(([prefix]) =>
key.startsWith(prefix), key.startsWith(prefix),
@@ -133,10 +158,8 @@ export const buildClientParams = (
(params[slot] as Record<string, unknown>)[ (params[slot] as Record<string, unknown>)[
key.slice(prefix.length) key.slice(prefix.length)
] = value; ] = value;
} else { } else if ('allowExtra' in config && config.allowExtra) {
for (const [slot, allowed] of Object.entries( for (const [slot, allowed] of Object.entries(config.allowExtra)) {
config.allowExtra ?? {},
)) {
if (allowed) { if (allowed) {
(params[slot as Slot] as Record<string, unknown>)[key] = value; (params[slot as Slot] as Record<string, unknown>)[key] = value;
break; break;

View File

@@ -8,7 +8,7 @@
* *
* For custom handler behavior, use src/mocks/handlers/overrides.ts * For custom handler behavior, use src/mocks/handlers/overrides.ts
* *
* Generated: 2025-11-26T12:21:51.098Z * Generated: 2026-03-01T17:00:19.178Z
*/ */
import { http, HttpResponse, delay } from 'msw'; import { http, HttpResponse, delay } from 'msw';