Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .agents/context/project.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ Read them before making changes; if they disagree with this file, they win.
- Framework: Lit 3 custom elements with Shadow DOM, `@lit/context` for shared state
- Positioning: `@floating-ui/dom`; localization: `igniteui-i18n-core`
- Styles: SCSS compiled to generated `.css.ts` files, themes from `igniteui-theming`
- Tests: Web Test Runner + Playwright, `@open-wc/testing`, mandatory a11y audits
- Tests: Web Test Runner + Playwright, `@open-wc/testing`, mandatory a11y audits, fast-check
property-based (fuzz) tests in `*.property.spec.ts`
- Docs and demos: Storybook, Custom Elements Manifest, TypeDoc
- Tooling: oxlint, oxfmt, stylelint, lit-analyzer, dependency-cruiser

Expand Down
2 changes: 2 additions & 0 deletions .agents/skills/review-component-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Map each change to a spec section with
- [ ] Interaction uses `#internals/testing/simulate.spec.js`. Forms use
`createFormAssociatedTestBed` and the validity helpers.
- [ ] No spec imports another component's spec. Shared helpers are in `src/internals/testing/`.
- [ ] A new or changed parser, converter or serializer has property-based tests in
`[module].property.spec.ts`
- [ ] The story's `// region default` block was regenerated (`cem` + `build:meta`), not edited
- [ ] CHANGELOG updated

Expand Down
22 changes: 21 additions & 1 deletion .github/CODING_GUIDELINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,11 @@ Each component has tests in `[component].spec.ts`. They cover:
| -------------------------- | ------------------------------------------------------------------------------ |
| `simulate.spec.js` | `simulateClick`, `simulateKeyboard`, `simulatePointerDown`, `simulateInput`, … |
| `form-testbed.spec.js` | `createFormAssociatedTestBed` and the shared label and ARIA projection suites |
| `validity-helpers.spec.js` | Validity assertions and `runValidationContainerTests` |
| `validity-helpers.spec.js` | Validity assertions and `runValidationContainerTests`. Await it. |
| `invoker-commands.spec.js` | `runInvokerCommandsTests` |
| `helpers.spec.js` | Animation, focus, scroll and style helpers, and `axeReflectedRelationsOptions` |
| `fast-check-setup.spec.js` | `fc` with the seed, `SLOW_PROPERTY_RUNS`, `orderedPair` and `withFixture` |
| `date-arbitraries.spec.js` | Date arbitraries, wall clocks and `assumeWallClock` for date properties |

Use the simulated events, not `element.click()` or a hand-built `KeyboardEvent`. They send
the full event sequence of a real user interaction.
Expand All @@ -548,6 +550,24 @@ Each component has tests in `[component].spec.ts`. They cover:
- The `## Test scenarios` section of the [specification](#specifications) mirrors the suite.
When you add or remove a test, update that section in the same change.

### Property-based tests

A parser, converter or serializer that takes user or stored input also gets property-based
(fuzz) tests with [fast-check](https://fast-check.dev/):

- Put them in `[module].property.spec.ts` next to the module. Import `fc` from
`#internals/testing/fast-check-setup.spec.js`, which sets the seed.
- Test properties that hold for all input: round-trips, invariants, and no throws. Where
possible, use an oracle that does not use the code under test.
- Generate Unicode input (`fc.string({ unit: 'grapheme' })`), not only ASCII.
- Keep each test well below the 3000 ms timeout. A property that renders components uses
`{ numRuns: SLOW_PROPERTY_RUNS }` and `withFixture`, which gives each run a new fixture.
- Use `Object.is` for values that can be `NaN`. Chai `equal` uses `===`.
- A date property with defaulted parts uses `assumeWallClock`. It skips a wall clock in a
daylight saving gap. CI runs in UTC and does not show these cases.
- When a property fails, fix the code and add the counterexample to the example-based suite.
See [CONTRIBUTING.md](CONTRIBUTING.md#property-based-tests) to replay it.

## Properties and Attributes

- Property names are camelCase. Attribute names are kebab-case. Properties that copy standard
Expand Down
18 changes: 18 additions & 0 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ To run the tests in watch mode, run:
npm run test:watch
```

### Property-based tests

Parsers, converters and serializers that take user or stored input also have property-based (fuzz) tests with [fast-check](https://fast-check.dev/), in `[module].property.spec.ts`. They run with the rest of the suite. fast-check shrinks a failure to a minimal counterexample.

`npm run test` uses a fixed seed, so a run fails only for a counterexample that your change causes. Each week, the [Fuzz workflow](workflows/fuzz.yml) uses a random seed and more runs. To do the same locally, set:

| Variable | Effect |
| -------------- | ----------------------------------------------------------- |
| `FC_SEED` | The seed. `random` picks a new one. |
| `FC_NUM_RUNS` | The number of runs for each property. The default is 100. |
| `TEST_TIMEOUT` | The test timeout in milliseconds, for a high `FC_NUM_RUNS`. |

```sh
FC_SEED=random FC_NUM_RUNS=1000 TEST_TIMEOUT=120000 npx wtr --files "src/**/*.property.spec.ts"
```

A failed property shows its seed and counterexample. To replay it, use the same seed and run count: `FC_SEED=<seed> FC_NUM_RUNS=<runs> npx wtr --files <spec>`. The weekly run uses 1000. Fix the code, and add the counterexample to the example-based suite. Change a property only if the property is wrong.

### Demoing with Storybook

To start a local instance of Storybook for your component, run:
Expand Down
28 changes: 28 additions & 0 deletions .github/actions/setup-playwright/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Set up Playwright
description: Restores or installs the Playwright Chromium browser and its system dependencies. Run it after `npm ci`.

runs:
using: composite
steps:
- name: Resolve the Playwright version
id: playwright
shell: bash
run: echo "version=$(node -p "require('playwright/package.json').version")" >> "$GITHUB_OUTPUT"

- name: Restore the Playwright browsers
id: playwright-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}

# The cache holds the browser binaries, but not the system libraries in the
# image, so `install-deps` runs in both cases.
- name: Install the Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
shell: bash
run: npx playwright install --with-deps --only-shell chromium
- name: Install the Playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
shell: bash
run: npx playwright install-deps chromium
2 changes: 2 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ structural reference.

- Write tests with `@open-wc/testing` in `[name].spec.ts`. The a11y audit is mandatory.
- Use the shared helpers in `src/internals/testing/`.
- A parser, converter or serializer also gets property-based tests with fast-check in
`[name].property.spec.ts`.
- Run `npm run check`, `npm run lint` and `npm run test` before you open a PR.

## Resources
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Runs the property-based tests with a random seed and more runs, to find new
# counterexamples. See CONTRIBUTING.md to replay a failure.
name: Fuzz

permissions: read-all

on:
schedule:
# Sundays at 02:30 UTC.
- cron: '30 2 * * 0'
workflow_dispatch:
inputs:
seed:
description: 'fast-check seed, or "random"'
default: 'random'
num-runs:
description: 'Runs for each property'
default: '1000'

jobs:
fuzz:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
cache: 'npm'
- run: npm ci

- uses: ./.github/actions/setup-playwright

# The tile-manager suite needs the compiled styles.
- run: npm run build:styles

- name: Run the property-based tests
env:
FC_SEED: ${{ inputs.seed || 'random' }}
FC_NUM_RUNS: ${{ inputs.num-runs || '1000' }}
TEST_TIMEOUT: '120000'
run: npx wtr --files "src/**/*.property.spec.ts"
21 changes: 1 addition & 20 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,7 @@ jobs:
cache: 'npm'
- run: npm ci

- name: Resolve the Playwright version
id: playwright
run: echo "version=$(node -p "require('playwright/package.json').version")" >> "$GITHUB_OUTPUT"

- name: Restore the Playwright browsers
id: playwright-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ steps.playwright.outputs.version }}

# The browser binaries live in the cache, but the system libraries they link
# against are installed into the image and cannot be, so `install-deps` runs
# either way.
- name: Install the Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps --only-shell chromium
- name: Install the Playwright system dependencies
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
- uses: ./.github/actions/setup-playwright

- run: npm run test

Expand Down
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Security
- #### Tile manager
- `loadLayout` now copies only the serialized tile properties: the spans, the positions, the flags and `id`. A layout from storage or a server can no longer set `innerHTML` on a tile or replace its prototype through `__proto__`. A value that is not an array, and an entry that is not an object, are ignored.

### Fixed
- #### QR code
- Versions 30 to 40 at the `M` error correction level now use the data codeword counts of ISO/IEC 18004. Before, these codes had the wrong block structure.
- #### Mask input, Date time input, Date range picker
- A mask position holds one UTF-16 code unit, so an astral character, such as an emoji, is now rejected as input and as a prompt. Before, it shifted the positions after it or split into two halves. A mask or input format with an astral literal now edits at the correct positions.
- #### Mask input
- An empty optional control is no longer a bad input. A value that fits no position of a letter mask, such as `12` for `LLL`, is now a bad input.
- #### Date time input, Date range picker
- A mask flag in the input format, such as `A` or `0`, now stays literal. The letters of a date range separator, such as the `t` of `' to '`, also stay literal.
- A `yyyy` year of three or four typed digits is now kept, also in the calendar: `0049` is year 49, and `02/29/0000` is valid. One or two typed digits still resolve to the 1950 to 2049 range.
- A `y` or `yyy` year format now widens to four characters in the mask and in the default placeholder, and keeps its case.
- #### Date range picker
- Setting `min` or `max` with a value before the first render no longer throws a `TypeError`.
- #### Color picker
- The HSL saturation no longer becomes infinite for a very small saturation at full value.

## [7.4.1] - 2026-09-25
### Added
- #### AI-Assisted Development
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ Read [ACCESSIBILITY.md][Accessibility] for the conformance target, the verificat

Security fixes are released for the latest major version, and critical fixes are backported to the previous major. Report vulnerabilities privately through [GitHub private vulnerability reporting](https://github.com/IgniteUI/igniteui-webcomponents/security/advisories/new), never in a public issue.

Every release ships with supply-chain evidence attached to the [GitHub release](https://github.com/IgniteUI/igniteui-webcomponents/releases): the published tarball with its digests, a CycloneDX SBOM, and signed provenance and SBOM attestations that you can check with `gh attestation verify`. GitHub's CodeQL default setup scans every push and pull request, the OpenSSF Scorecard runs weekly, and Dependabot keeps dependencies and actions patched.
Every release ships with supply-chain evidence attached to the [GitHub release](https://github.com/IgniteUI/igniteui-webcomponents/releases): the published tarball with its digests, a CycloneDX SBOM, and signed provenance and SBOM attestations that you can check with `gh attestation verify`. GitHub's CodeQL default setup scans every push and pull request, the OpenSSF Scorecard runs weekly, and Dependabot keeps dependencies and actions patched. Property-based (fuzz) tests with [fast-check](https://fast-check.dev/) check the mask, date, color, QR code and layout parsers on every push and pull request, and weekly with random seeds.

Read [SECURITY.md][Security] for the support policy, the reporting process, response targets and verification steps, and [THREAT-MODEL.md][Threat model] for the trust boundaries and what the host application remains responsible for.

Expand Down
4 changes: 4 additions & 0 deletions THREAT-MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ The components make no network requests of their own. `registerIcon` fetches the

The icon registry opens the `BroadcastChannel` `ignite-ui-icon-channel` and publishes the icons the application registers to other browsing contexts of the same origin. It never applies state it receives, so another context cannot inject an icon. Nothing leaves the origin.

### Untrusted serialized layouts

`loadLayout` on the tile manager treats a layout as untrusted, because applications keep it in web storage or on a server. It copies only the serialized tile properties (spans, positions, flags and `id`) to the tiles with a matching `id`. It ignores all other keys, such as `innerHTML` or an own `__proto__`, a value that is not an array, and an entry that is not an object. Invalid JSON throws a `SyntaxError`. Property-based tests send generated and hostile layouts to it.

### Clipboard

The color picker and the chat message actions write to the clipboard only when the user activates a copy control. The components never read the clipboard, so pasted content reaches them only as ordinary user input.
Expand Down
41 changes: 41 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
"custom-element-vs-code-integration": "^1.5.0",
"custom-elements-manifest": "^2.1.0",
"dependency-cruiser": "^18.4.0",
"fast-check": "^4.10.2",
"husky": "^9.1.7",
"ig-typedoc-theme": "^7.0.1",
"igniteui-i18n-resources": "^1.0.5",
Expand Down
2 changes: 1 addition & 1 deletion src/components/checkbox/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ describe('Checkbox', () => {
{ slots: ['invalid'], props: { required: true } }, // invalid slot
];

runValidationContainerTests(IgcCheckboxComponent, testParameters);
await runValidationContainerTests(IgcCheckboxComponent, testParameters);
});
});

Expand Down
7 changes: 5 additions & 2 deletions src/components/color-picker/color-picker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1336,15 +1336,18 @@ describe('Color picker', () => {
});

describe('Validation message slots', () => {
it('renders validation message slots', () => {
it('renders validation message slots', async () => {
const testParameters: ValidationContainerTestsParams<IgcColorPickerComponent>[] =
[
{ slots: ['valueMissing'], props: { required: true } },
{ slots: ['customError'] },
{ slots: ['invalid'], props: { required: true } },
];

runValidationContainerTests(IgcColorPickerComponent, testParameters);
await runValidationContainerTests(
IgcColorPickerComponent,
testParameters
);
});
});

Expand Down
Loading
Loading