Compare commits
2 Commits
ff7a67cb58
...
07309013d7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07309013d7 | ||
|
|
846fc31190 |
@@ -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):**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=="],
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -99,6 +99,5 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"glob": "^10.4.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
Reference in New Issue
Block a user