aria-ease 6.11.0 → 6.12.1

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.
Files changed (37) hide show
  1. package/README.md +3 -3
  2. package/dist/{ComboboxComponentStrategy-OGRVZXAF.js → ComboboxComponentStrategy-DU342VMB.js} +7 -7
  3. package/dist/RelativeTargetResolver-DJAITO6D.js +7 -0
  4. package/dist/{audit-RM6TCZ5C.js → audit-JYEPKLHR.js} +5 -0
  5. package/dist/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +0 -23
  6. package/dist/{chunk-NI3MQCAS.js → chunk-GJGUY643.js} +2 -2
  7. package/dist/chunk-GLT43UVH.js +43 -0
  8. package/dist/cli.cjs +147 -84
  9. package/dist/cli.js +5 -5
  10. package/dist/{configLoader-DWHOHXHL.js → configLoader-Q7N5XV4P.js} +2 -2
  11. package/dist/{configLoader-UJZHQBYS.js → configLoader-REHK3S3Q.js} +1 -1
  12. package/dist/{contractTestRunnerPlaywright-QDXSK3FE.js → contractTestRunnerPlaywright-H24LQ45R.js} +113 -72
  13. package/dist/{contractTestRunnerPlaywright-WNWQYSXZ.js → contractTestRunnerPlaywright-NL3JNJYH.js} +113 -72
  14. package/dist/index.cjs +248 -112
  15. package/dist/index.d.cts +3 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +124 -41
  18. package/dist/src/combobox/index.cjs +1 -0
  19. package/dist/src/combobox/index.js +1 -0
  20. package/dist/src/utils/test/{ComboboxComponentStrategy-5AECQSRN.js → ComboboxComponentStrategy-XKQ72RFD.js} +7 -7
  21. package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +1 -0
  22. package/dist/src/utils/test/{chunk-XERMSYEH.js → chunk-4DU5Z5BR.js} +1 -23
  23. package/dist/src/utils/test/chunk-GLT43UVH.js +41 -0
  24. package/dist/src/utils/test/{configLoader-SHJSRG2A.js → configLoader-NA7IBCS3.js} +2 -2
  25. package/dist/src/utils/test/{contractTestRunnerPlaywright-Z2AHXSNM.js → contractTestRunnerPlaywright-5FT6K2WN.js} +111 -71
  26. package/dist/src/utils/test/dsl/index.cjs +106 -29
  27. package/dist/src/utils/test/dsl/index.d.cts +3 -0
  28. package/dist/src/utils/test/dsl/index.d.ts +3 -0
  29. package/dist/src/utils/test/dsl/index.js +106 -29
  30. package/dist/src/utils/test/index.cjs +135 -76
  31. package/dist/src/utils/test/index.js +17 -11
  32. package/dist/{test-O3J4ZPQR.js → test-FYSJXQWO.js} +17 -12
  33. package/package.json +3 -4
  34. package/dist/src/utils/test/aria-contracts/accordion/accordion.contract.json +0 -290
  35. package/dist/src/utils/test/aria-contracts/combobox/combobox.listbox.contract.json +0 -463
  36. package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +0 -562
  37. package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +0 -361
@@ -1,4 +1,5 @@
1
- import { contract_default, ContractReporter, normalizeStrictness, createTestPage, normalizeLevel, resolveEnforcement } from './chunk-XERMSYEH.js';
1
+ import { ContractReporter, normalizeStrictness, createTestPage, normalizeLevel, resolveEnforcement } from './chunk-4DU5Z5BR.js';
2
+ import { RelativeTargetResolver } from './chunk-GLT43UVH.js';
2
3
  import { readFileSync } from 'fs';
3
4
  import path2 from 'path';
4
5
  import { pathToFileURL } from 'url';
@@ -27,7 +28,7 @@ var StrategyRegistry = class {
27
28
  );
28
29
  this.builtInStrategies.set(
29
30
  "combobox",
30
- () => import('./ComboboxComponentStrategy-5AECQSRN.js').then(
31
+ () => import('./ComboboxComponentStrategy-XKQ72RFD.js').then(
31
32
  (m) => m.ComboboxComponentStrategy
32
33
  )
33
34
  );
@@ -37,12 +38,6 @@ var StrategyRegistry = class {
37
38
  (m) => m.TabsComponentStrategy
38
39
  )
39
40
  );
40
- this.builtInStrategies.set(
41
- "combobox.listbox",
42
- () => import('./ComboboxComponentStrategy-5AECQSRN.js').then(
43
- (m) => m.ComboboxComponentStrategy
44
- )
45
- );
46
41
  }
47
42
  /**
48
43
  * Load a strategy - either from custom path or built-in registry
@@ -106,11 +101,7 @@ var ComponentDetector = class {
106
101
  */
107
102
  static async detect(componentName, componentConfig, actionTimeoutMs = 400, assertionTimeoutMs = 400, configBaseDir) {
108
103
  const typedComponentConfig = this.isComponentConfig(componentConfig) ? componentConfig : void 0;
109
- let contractPath = typedComponentConfig?.path;
110
- if (!contractPath) {
111
- const contractTyped = contract_default;
112
- contractPath = contractTyped[componentName]?.path;
113
- }
104
+ const contractPath = typedComponentConfig?.contractPath;
114
105
  if (!contractPath) {
115
106
  throw new Error(`Contract path not found for component: ${componentName}`);
116
107
  }
@@ -143,7 +134,7 @@ var ComponentDetector = class {
143
134
  if (!strategyClass) {
144
135
  return null;
145
136
  }
146
- const mainSelector = selectors.trigger || selectors.input || selectors.tablist || selectors.container;
137
+ const mainSelector = selectors.main;
147
138
  if (componentName === "tabs") {
148
139
  return new strategyClass(mainSelector, selectors);
149
140
  }
@@ -156,46 +147,6 @@ var ComponentDetector = class {
156
147
  }
157
148
  };
158
149
 
159
- // src/utils/test/src/RelativeTargetResolver.ts
160
- var RelativeTargetResolver = class {
161
- /**
162
- * Resolve a relative target like "first", "second", "last", "next", "previous"
163
- * @param page Playwright page instance
164
- * @param selector Base selector to find elements
165
- * @param relative Relative position (first, second, last, next, previous)
166
- * @returns The resolved Locator or null if not found
167
- */
168
- static async resolve(page, selector, relative) {
169
- const items = await page.locator(selector).all();
170
- switch (relative) {
171
- case "first":
172
- return items[0];
173
- case "second":
174
- return items[1];
175
- case "last":
176
- return items[items.length - 1];
177
- case "next": {
178
- const currentIndex = await page.evaluate(([sel]) => {
179
- const items2 = Array.from(document.querySelectorAll(sel));
180
- return items2.indexOf(document.activeElement);
181
- }, [selector]);
182
- const nextIndex = (currentIndex + 1) % items.length;
183
- return items[nextIndex];
184
- }
185
- case "previous": {
186
- const currentIndex = await page.evaluate(([sel]) => {
187
- const items2 = Array.from(document.querySelectorAll(sel));
188
- return items2.indexOf(document.activeElement);
189
- }, [selector]);
190
- const prevIndex = (currentIndex - 1 + items.length) % items.length;
191
- return items[prevIndex];
192
- }
193
- default:
194
- return null;
195
- }
196
- }
197
- };
198
-
199
150
  // src/utils/test/src/ActionExecutor.ts
200
151
  var ActionExecutor = class {
201
152
  constructor(page, selectors, timeoutMs = 400) {
@@ -221,16 +172,16 @@ var ActionExecutor = class {
221
172
  async focus(target, relativeTarget, virtualId) {
222
173
  try {
223
174
  if (target === "virtual" && virtualId) {
224
- const inputSelector = this.selectors.input;
225
- if (!inputSelector) {
226
- return { success: false, error: `Input selector not defined for virtual focus.` };
175
+ const mainSelector = this.selectors.main;
176
+ if (!mainSelector) {
177
+ return { success: false, error: `Main selector not defined for virtual focus.` };
227
178
  }
228
- const input = this.page.locator(inputSelector).first();
229
- const exists = await input.count();
179
+ const main = this.page.locator(mainSelector).first();
180
+ const exists = await main.count();
230
181
  if (!exists) {
231
- return { success: false, error: `Input element not found for virtual focus.` };
182
+ return { success: false, error: `Main element not found for virtual focus.` };
232
183
  }
233
- await input.evaluate((el, id) => {
184
+ await main.evaluate((el, id) => {
234
185
  el.setAttribute("aria-activedescendant", id);
235
186
  }, virtualId);
236
187
  return { success: true };
@@ -522,6 +473,10 @@ var AssertionRunner = class {
522
473
  };
523
474
  }
524
475
  }
476
+ if (typeof expectedValue !== "string") {
477
+ console.error("[AssertionRunner] expectedValue is not a string:", expectedValue);
478
+ throw new Error(`AssertionRunner: expectedValue for attribute assertion must be a string, but got: ${JSON.stringify(expectedValue)}`);
479
+ }
525
480
  const expectedValues = expectedValue.split(" | ").map((v) => v.trim());
526
481
  const attributeValue = await target.getAttribute(attribute);
527
482
  if (attributeValue !== null && expectedValues.includes(attributeValue)) {
@@ -689,7 +644,7 @@ var AssertionRunner = class {
689
644
  // src/utils/test/src/contractTestRunnerPlaywright.ts
690
645
  async function runContractTestsPlaywright(componentName, url, strictness, config, configBaseDir) {
691
646
  const componentConfig = config?.test?.components?.find((c) => c.name === componentName);
692
- const isCustomContract = !!componentConfig?.path;
647
+ const isCustomContract = !!componentConfig?.contractPath;
693
648
  const reporter = new ContractReporter(true, isCustomContract);
694
649
  const defaultTimeouts = {
695
650
  actionTimeoutMs: 400,
@@ -729,11 +684,7 @@ async function runContractTestsPlaywright(componentName, url, strictness, config
729
684
  defaultTimeouts.componentReadyTimeoutMs
730
685
  );
731
686
  const strictnessMode = normalizeStrictness(strictness);
732
- let contractPath = componentConfig?.path;
733
- if (!contractPath) {
734
- const contractTyped = contract_default;
735
- contractPath = contractTyped[componentName]?.path;
736
- }
687
+ const contractPath = componentConfig?.contractPath;
737
688
  if (!contractPath) {
738
689
  throw new Error(`Contract path not found for component: ${componentName}`);
739
690
  }
@@ -952,6 +903,52 @@ This usually means:
952
903
  reporter.reportStaticTest(relDescription, "pass", void 0, relationshipLevel);
953
904
  }
954
905
  }
906
+ async function resolveExpectedValue(expectedValue, selectors, page2, context = {}) {
907
+ if (!expectedValue || typeof expectedValue !== "object" || !("ref" in expectedValue)) return expectedValue;
908
+ let refSelector;
909
+ if (expectedValue.ref === "relative") {
910
+ if (!expectedValue.relativeTarget || !context.relativeBaseSelector) return void 0;
911
+ const baseLocator = page2.locator(context.relativeBaseSelector);
912
+ const count = await baseLocator.count();
913
+ let idx = 0;
914
+ if (expectedValue.relativeTarget === "first") idx = 0;
915
+ else if (expectedValue.relativeTarget === "second") idx = 1;
916
+ else if (expectedValue.relativeTarget === "last") idx = count - 1;
917
+ else if (!isNaN(Number(expectedValue.relativeTarget))) idx = Number(expectedValue.relativeTarget);
918
+ else idx = 0;
919
+ if (idx < 0 || idx >= count) return void 0;
920
+ const relElem = baseLocator.nth(idx);
921
+ return await getPropertyFromLocator(relElem, expectedValue.property || expectedValue.attribute);
922
+ } else {
923
+ refSelector = selectors[expectedValue.ref];
924
+ if (!refSelector) throw new Error(`Selector for ref '${expectedValue.ref}' not found in contract selectors.`);
925
+ const refLocator = page2.locator(refSelector).first();
926
+ return await getPropertyFromLocator(refLocator, expectedValue.property || expectedValue.attribute);
927
+ }
928
+ }
929
+ async function getPropertyFromLocator(locator, property) {
930
+ if (!locator) return void 0;
931
+ if (!property || property === "id") {
932
+ return await locator.getAttribute("id") ?? void 0;
933
+ } else if (property === "class") {
934
+ return await locator.getAttribute("class") ?? void 0;
935
+ } else if (property === "textContent") {
936
+ return await locator.evaluate((el) => el.textContent ?? void 0);
937
+ } else if (property.startsWith("aria-")) {
938
+ return await locator.getAttribute(property) ?? void 0;
939
+ } else if (property.endsWith("*")) {
940
+ const attrs = await locator.evaluate((el) => {
941
+ const out = [];
942
+ for (const attr of Array.from(el.attributes)) {
943
+ if (attr.name.startsWith("aria-")) out.push(`${attr.name}=${attr.value}`);
944
+ }
945
+ return out.join(";");
946
+ });
947
+ return attrs;
948
+ } else {
949
+ return await locator.getAttribute(property) ?? void 0;
950
+ }
951
+ }
955
952
  const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
956
953
  for (const test of componentContract.static[0]?.assertions || []) {
957
954
  if (test.target === "relative") continue;
@@ -994,6 +991,22 @@ This usually means:
994
991
  }
995
992
  return false;
996
993
  };
994
+ let expectedValue = test.expectedValue;
995
+ if (test.expectedValue && typeof test.expectedValue === "object" && "ref" in test.expectedValue) {
996
+ const context = {};
997
+ const relTarget = test.relativeTarget;
998
+ if (test.expectedValue.ref === "relative" && test.target === "relative" && relTarget) {
999
+ const baseSel = componentContract.selectors[relTarget];
1000
+ if (!baseSel) throw new Error(`Selector for relativeTarget '${relTarget}' not found in contract selectors.`);
1001
+ context.relativeBaseSelector = baseSel;
1002
+ } else if (test.expectedValue.ref === "relative" && relTarget) {
1003
+ const baseSel = componentContract.selectors[relTarget];
1004
+ if (!baseSel) throw new Error(`Selector for relativeTarget '${relTarget}' not found in contract selectors.`);
1005
+ context.relativeBaseSelector = baseSel;
1006
+ }
1007
+ expectedValue = await resolveExpectedValue(test.expectedValue, componentContract.selectors, page, context);
1008
+ console.log("Expected value in static check", expectedValue);
1009
+ }
997
1010
  if (!test.expectedValue) {
998
1011
  const attributes = test.attribute.split(" | ");
999
1012
  let hasAny = false;
@@ -1027,16 +1040,17 @@ This usually means:
1027
1040
  reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
1028
1041
  }
1029
1042
  } else {
1030
- if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
1031
- passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
1043
+ if (isRedundantCheck(targetSelector, test.attribute, typeof expectedValue === "string" ? expectedValue : void 0)) {
1044
+ passes.push(`${test.attribute}="${expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
1032
1045
  staticPassed += 1;
1033
1046
  reporter.reportStaticTest(staticDescription, "pass", void 0, staticLevel);
1034
1047
  } else {
1048
+ const valueToCheck = expectedValue ?? "";
1035
1049
  const result = await staticAssertionRunner.validateAttribute(
1036
1050
  target,
1037
1051
  test.target,
1038
1052
  test.attribute,
1039
- test.expectedValue,
1053
+ valueToCheck,
1040
1054
  test.failureMessage,
1041
1055
  "Static ARIA Test"
1042
1056
  );
@@ -1154,7 +1168,33 @@ This usually means:
1154
1168
  continue;
1155
1169
  }
1156
1170
  for (const assertion of assertions) {
1157
- const result = await assertionRunner.validate(assertion, dynamicTest.description);
1171
+ let expectedValue;
1172
+ if (assertion.expectedValue && typeof assertion.expectedValue === "object" && "ref" in assertion.expectedValue) {
1173
+ if (assertion.expectedValue.ref === "relative") {
1174
+ const { RelativeTargetResolver: RelativeTargetResolver2 } = await import('./RelativeTargetResolver-G2XDN2VV.js');
1175
+ const relativeSelector = componentContract.selectors.relative;
1176
+ if (!relativeSelector) throw new Error("Relative selector not defined in contract selectors.");
1177
+ const relTarget = assertion.relativeTarget || "first";
1178
+ const relElem = await RelativeTargetResolver2.resolve(page, relativeSelector, relTarget);
1179
+ if (!relElem) throw new Error(`Could not resolve relative target '${relTarget}' for expectedValue.`);
1180
+ const prop = assertion.expectedValue.property || assertion.expectedValue.attribute || "id";
1181
+ if (prop === "textContent") {
1182
+ expectedValue = await relElem.evaluate((el) => el.textContent ?? void 0);
1183
+ } else {
1184
+ const attr = await relElem.getAttribute(prop);
1185
+ expectedValue = attr === null ? void 0 : attr;
1186
+ }
1187
+ } else {
1188
+ expectedValue = await resolveExpectedValue(assertion.expectedValue, componentContract.selectors, page, {});
1189
+ }
1190
+ } else if (typeof assertion.expectedValue === "string" || typeof assertion.expectedValue === "undefined") {
1191
+ expectedValue = assertion.expectedValue;
1192
+ } else {
1193
+ expectedValue = "";
1194
+ }
1195
+ const assertionToRun = { ...assertion, expectedValue };
1196
+ const valueToCheck = expectedValue ?? "";
1197
+ const result = await assertionRunner.validate({ ...assertionToRun, expectedValue: valueToCheck }, dynamicTest.description);
1158
1198
  if (result.success && result.passMessage) {
1159
1199
  passes.push(result.passMessage);
1160
1200
  } else if (!result.success && result.failMessage) {
@@ -18,7 +18,7 @@ function resolveSetup(setup, ctx) {
18
18
  );
19
19
  }
20
20
  var COMBOBOX_STATES = {
21
- "listbox.open": {
21
+ "popup.open": {
22
22
  setup: [
23
23
  {
24
24
  when: ["keyboard", "textInput"],
@@ -35,7 +35,7 @@ var COMBOBOX_STATES = {
35
35
  ],
36
36
  assertion: isComboboxOpen
37
37
  },
38
- "listbox.closed": {
38
+ "popup.closed": {
39
39
  setup: [
40
40
  {
41
41
  when: ["keyboard"],
@@ -50,18 +50,18 @@ var COMBOBOX_STATES = {
50
50
  ]
51
51
  }
52
52
  ],
53
- assertion: isComboboxClosed
53
+ assertion: [...isComboboxClosed(), ...isActiveDescendantEmpty()]
54
54
  },
55
- "input.focused": {
55
+ "main.focused": {
56
56
  setup: [
57
57
  {
58
58
  when: ["keyboard"],
59
59
  steps: () => [
60
- { type: "focus", target: "input" }
60
+ { type: "focus", target: "main" }
61
61
  ]
62
62
  }
63
63
  ],
64
- assertion: isInputFocused
64
+ assertion: isMainFocused
65
65
  },
66
66
  "input.filled": {
67
67
  setup: [
@@ -74,8 +74,19 @@ var COMBOBOX_STATES = {
74
74
  ],
75
75
  assertion: isInputFilled
76
76
  },
77
+ "input.notFilled": {
78
+ setup: [
79
+ {
80
+ when: ["keyboard", "textInput"],
81
+ steps: () => [
82
+ { type: "type", target: "input", value: "" }
83
+ ]
84
+ }
85
+ ],
86
+ assertion: isInputNotFilled
87
+ },
77
88
  "activeOption.first": {
78
- requires: ["listbox.open"],
89
+ requires: ["popup.open"],
79
90
  setup: [
80
91
  {
81
92
  when: ["keyboard"],
@@ -84,7 +95,7 @@ var COMBOBOX_STATES = {
84
95
  ]
85
96
  }
86
97
  ],
87
- assertion: isActiveDescendantNotEmpty
98
+ assertion: isActiveDescendantFirst
88
99
  },
89
100
  "activeOption.last": {
90
101
  requires: ["activeOption.first"],
@@ -96,10 +107,30 @@ var COMBOBOX_STATES = {
96
107
  ]
97
108
  }
98
109
  ],
110
+ assertion: isActiveDescendantLast
111
+ },
112
+ "activeDescendant.notEmpty": {
113
+ requires: [],
114
+ setup: [
115
+ {
116
+ when: ["keyboard"],
117
+ steps: () => []
118
+ }
119
+ ],
99
120
  assertion: isActiveDescendantNotEmpty
100
121
  },
122
+ "activeDescendant.Empty": {
123
+ requires: [],
124
+ setup: [
125
+ {
126
+ when: ["keyboard"],
127
+ steps: () => []
128
+ }
129
+ ],
130
+ assertion: isActiveDescendantEmpty
131
+ },
101
132
  "selectedOption.first": {
102
- requires: ["listbox.open"],
133
+ requires: ["popup.open"],
103
134
  setup: [
104
135
  {
105
136
  when: ["pointer"],
@@ -111,7 +142,7 @@ var COMBOBOX_STATES = {
111
142
  assertion: () => isAriaSelected("first")
112
143
  },
113
144
  "selectedOption.last": {
114
- requires: ["listbox.open"],
145
+ requires: ["popup.open"],
115
146
  setup: [
116
147
  {
117
148
  when: ["pointer"],
@@ -126,43 +157,76 @@ var COMBOBOX_STATES = {
126
157
  function isComboboxOpen() {
127
158
  return [
128
159
  {
129
- target: "listbox",
160
+ target: "popup",
130
161
  assertion: "toBeVisible",
131
- failureMessage: "Expected listbox to be visible"
162
+ failureMessage: "Expected popup to be visible"
132
163
  },
133
164
  {
134
- target: "input",
165
+ target: "main",
135
166
  assertion: "toHaveAttribute",
136
167
  attribute: "aria-expanded",
137
168
  expectedValue: "true",
138
- failureMessage: "Expect combobox input to have aria-expanded='true'"
169
+ failureMessage: "Expect combobox main to have aria-expanded='true'."
139
170
  }
140
171
  ];
141
172
  }
142
173
  function isComboboxClosed() {
143
174
  return [
144
175
  {
145
- target: "listbox",
176
+ target: "popup",
146
177
  assertion: "notToBeVisible",
147
- failureMessage: "Expected listbox to be closed"
178
+ failureMessage: "Expected popup to be closed"
148
179
  },
149
180
  {
150
- target: "input",
181
+ target: "main",
151
182
  assertion: "toHaveAttribute",
152
183
  attribute: "aria-expanded",
153
184
  expectedValue: "false",
154
- failureMessage: "Expect combobox input to have aria-expanded='false'"
185
+ failureMessage: "Expect combobox main to have aria-expanded='false'."
186
+ }
187
+ ];
188
+ }
189
+ function isActiveDescendantFirst() {
190
+ return [
191
+ {
192
+ target: "main",
193
+ assertion: "toHaveAttribute",
194
+ attribute: "aria-activedescendant",
195
+ expectedValue: { ref: "relative", relativeTarget: "first", property: "id" },
196
+ failureMessage: "Expected aria-activedescendant on main to match the id of the first option."
197
+ }
198
+ ];
199
+ }
200
+ function isActiveDescendantLast() {
201
+ return [
202
+ {
203
+ target: "main",
204
+ assertion: "toHaveAttribute",
205
+ attribute: "aria-activedescendant",
206
+ expectedValue: { ref: "relative", relativeTarget: "last", property: "id" },
207
+ failureMessage: "Expected aria-activedescendant on main to match the id of the last option."
155
208
  }
156
209
  ];
157
210
  }
158
211
  function isActiveDescendantNotEmpty() {
159
212
  return [
160
213
  {
161
- target: "input",
214
+ target: "main",
162
215
  assertion: "toHaveAttribute",
163
216
  attribute: "aria-activedescendant",
164
217
  expectedValue: "!empty",
165
- failureMessage: "Expected aria-activedescendant to not be empty"
218
+ failureMessage: "Expected aria-activedescendant on main to not be empty."
219
+ }
220
+ ];
221
+ }
222
+ function isActiveDescendantEmpty() {
223
+ return [
224
+ {
225
+ target: "main",
226
+ assertion: "toHaveAttribute",
227
+ attribute: "aria-activedescendant",
228
+ expectedValue: "",
229
+ failureMessage: "Expected aria-activedescendant on main to be empty."
166
230
  }
167
231
  ];
168
232
  }
@@ -174,16 +238,16 @@ function isAriaSelected(index) {
174
238
  assertion: "toHaveAttribute",
175
239
  attribute: "aria-selected",
176
240
  expectedValue: "true",
177
- failureMessage: `Expected ${index} option to have aria-selected='true'`
241
+ failureMessage: `Expected ${index} option to have aria-selected='true'.`
178
242
  }
179
243
  ];
180
244
  }
181
- function isInputFocused() {
245
+ function isMainFocused() {
182
246
  return [
183
247
  {
184
- target: "input",
248
+ target: "main",
185
249
  assertion: "toHaveFocus",
186
- failureMessage: "Expected input to be focused"
250
+ failureMessage: "Expected main to be focused."
187
251
  }
188
252
  ];
189
253
  }
@@ -193,14 +257,24 @@ function isInputFilled() {
193
257
  target: "input",
194
258
  assertion: "toHaveValue",
195
259
  expectedValue: "test",
196
- failureMessage: "Expected input to have the value 'test'"
260
+ failureMessage: "Expected input to have the value 'test'."
261
+ }
262
+ ];
263
+ }
264
+ function isInputNotFilled() {
265
+ return [
266
+ {
267
+ target: "input",
268
+ assertion: "toHaveValue",
269
+ expectedValue: "",
270
+ failureMessage: "Expected input to have the value ''."
197
271
  }
198
272
  ];
199
273
  }
200
274
 
201
275
  // src/utils/test/dsl/src/contractBuilder.ts
202
276
  var STATE_PACKS = {
203
- "combobox.listbox": COMBOBOX_STATES
277
+ "combobox": COMBOBOX_STATES
204
278
  // Add more mappings as needed
205
279
  };
206
280
  var FluentContract = class {
@@ -234,11 +308,13 @@ var ContractBuilder = class {
234
308
  const api = {
235
309
  ariaReference: (from, attribute, to) => ({
236
310
  required: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "required" }),
237
- optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" })
311
+ optional: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "optional" }),
312
+ recommended: () => this.relationshipInvariants.push({ type: "aria-reference", from, attribute, to, level: "recommended" })
238
313
  }),
239
314
  contains: (parent, child) => ({
240
315
  required: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "required" }),
241
- optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" })
316
+ optional: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "optional" }),
317
+ recommended: () => this.relationshipInvariants.push({ type: "contains", parent, child, level: "recommended" })
242
318
  })
243
319
  };
244
320
  fn(api);
@@ -249,7 +325,8 @@ var ContractBuilder = class {
249
325
  target: (target) => ({
250
326
  has: (attribute, expectedValue) => ({
251
327
  required: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "required" }),
252
- optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" })
328
+ optional: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "optional" }),
329
+ recommended: () => this.staticAssertions.push({ target, attribute, expectedValue, failureMessage: "", level: "recommended" })
253
330
  })
254
331
  })
255
332
  };
@@ -92,10 +92,12 @@ declare class ContractBuilder {
92
92
  ariaReference: (from: string, attribute: string, to: string) => {
93
93
  required: () => void;
94
94
  optional: () => void;
95
+ recommended: () => void;
95
96
  };
96
97
  contains: (parent: string, child: string) => {
97
98
  required: () => void;
98
99
  optional: () => void;
100
+ recommended: () => void;
99
101
  };
100
102
  }) => void): this;
101
103
  static(fn: (s: {
@@ -103,6 +105,7 @@ declare class ContractBuilder {
103
105
  has: (attribute: string, expectedValue: string) => {
104
106
  required: () => void;
105
107
  optional: () => void;
108
+ recommended: () => void;
106
109
  };
107
110
  };
108
111
  }) => void): this;
@@ -92,10 +92,12 @@ declare class ContractBuilder {
92
92
  ariaReference: (from: string, attribute: string, to: string) => {
93
93
  required: () => void;
94
94
  optional: () => void;
95
+ recommended: () => void;
95
96
  };
96
97
  contains: (parent: string, child: string) => {
97
98
  required: () => void;
98
99
  optional: () => void;
100
+ recommended: () => void;
99
101
  };
100
102
  }) => void): this;
101
103
  static(fn: (s: {
@@ -103,6 +105,7 @@ declare class ContractBuilder {
103
105
  has: (attribute: string, expectedValue: string) => {
104
106
  required: () => void;
105
107
  optional: () => void;
108
+ recommended: () => void;
106
109
  };
107
110
  };
108
111
  }) => void): this;