anu-verzum 1.21.6 → 1.22.0

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 (50) hide show
  1. package/README.md +46 -6
  2. package/dist/core/components/AnulyticsProvider.d.ts +3 -0
  3. package/dist/core/components/AnulyticsProvider.js +66 -58
  4. package/dist/core/components/History.d.ts +3 -0
  5. package/dist/core/components/History.js +10 -2
  6. package/dist/core/components/Intl.d.ts +3 -0
  7. package/dist/core/components/Intl.js +13 -2
  8. package/dist/core/elements.d.ts +1 -1
  9. package/dist/core/reconciler.d.ts +5 -0
  10. package/dist/core/reconciler.js +41 -2
  11. package/dist/testing/__tests__/smoke.test.d.ts +1 -0
  12. package/dist/testing/__tests__/smoke.test.js +180 -0
  13. package/dist/testing/act.d.ts +4 -0
  14. package/dist/testing/act.js +42 -0
  15. package/dist/testing/cleanup.d.ts +3 -0
  16. package/dist/testing/cleanup.js +32 -0
  17. package/dist/testing/events/fireEvent.d.ts +17 -0
  18. package/dist/testing/events/fireEvent.js +45 -0
  19. package/dist/testing/events/userEvent.d.ts +12 -0
  20. package/dist/testing/events/userEvent.js +67 -0
  21. package/dist/testing/globals.d.js +1 -0
  22. package/dist/testing/index.d.ts +9 -0
  23. package/dist/testing/index.js +109 -0
  24. package/dist/testing/queries/byAltText.d.ts +2 -0
  25. package/dist/testing/queries/byAltText.js +15 -0
  26. package/dist/testing/queries/byLabelText.d.ts +2 -0
  27. package/dist/testing/queries/byLabelText.js +47 -0
  28. package/dist/testing/queries/byPlaceholderText.d.ts +2 -0
  29. package/dist/testing/queries/byPlaceholderText.js +15 -0
  30. package/dist/testing/queries/byRole.d.ts +2 -0
  31. package/dist/testing/queries/byRole.js +73 -0
  32. package/dist/testing/queries/byTestId.d.ts +2 -0
  33. package/dist/testing/queries/byTestId.js +11 -0
  34. package/dist/testing/queries/byText.d.ts +2 -0
  35. package/dist/testing/queries/byText.js +27 -0
  36. package/dist/testing/queries/byTitle.d.ts +2 -0
  37. package/dist/testing/queries/byTitle.js +15 -0
  38. package/dist/testing/queries/index.d.ts +2 -0
  39. package/dist/testing/queries/index.js +43 -0
  40. package/dist/testing/queries/queryBuilder.d.ts +2 -0
  41. package/dist/testing/queries/queryBuilder.js +36 -0
  42. package/dist/testing/render.d.ts +3 -0
  43. package/dist/testing/render.js +46 -0
  44. package/dist/testing/types.d.ts +59 -0
  45. package/dist/testing/types.js +5 -0
  46. package/dist/testing/waitFor.d.ts +3 -0
  47. package/dist/testing/waitFor.js +41 -0
  48. package/dist/testing/wrappers.d.ts +17 -0
  49. package/dist/testing/wrappers.js +46 -0
  50. package/package.json +12 -2
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <h3>@author: <strong>Anubis-programmer</strong></h3>
6
6
  <h3>@license: <strong>MIT</strong></h3>
7
- <h3>@version: <strong>1.21.6</strong></h3>
7
+ <h3>@version: <strong>1.22.0</strong></h3>
8
8
 
9
9
  <br>
10
10
 
@@ -17,6 +17,7 @@ A lightweight React-inspired UI library for building component-based web applica
17
17
  - Client-side routing over the History API
18
18
  - Context API, i18n (Intl), feature flags, and built-in event analytics (Anulytics)
19
19
  - Ships with TypeScript declaration files — no `@types` package needed
20
+ - Built-in testing companion — **Anu Testing Library (ATL)** shipped as `anu-verzum/testing`
20
21
 
21
22
  <br>
22
23
  <hr>
@@ -143,6 +144,18 @@ Create `tsconfig.json`:
143
144
  | `moduleResolution` | `"bundler"` | Correct setting for Webpack/Babel projects |
144
145
  | `target` | `"ES2018"` | Because Babel handles compilation (`noEmit: true`), `target` only controls which TypeScript built-in type definitions are available — it does not affect emitted code. ES2018 is the minimum required to include `Promise.prototype.finally` on values returned by `Anu.ServerAPI` methods. |
145
146
 
147
+ #### Typing `process.env`
148
+
149
+ TypeScript does not know about `process` in a browser project by default — it is a Node.js global. If you reference `process.env.SOME_VAR` in your source (for example, to pass a value injected by webpack `DefinePlugin`), add a declaration file so the type checker can resolve it without pulling in the full Node.js type surface:
150
+
151
+ Create `src/env.d.ts`:
152
+
153
+ ```typescript
154
+ declare const process: { env: Record<string, string | undefined> };
155
+ ```
156
+
157
+ This is enough for any `process.env.*` access. If your project already depends on `@types/node` for other reasons, you can skip the declaration file and add `"types": ["node"]` to `compilerOptions` in `tsconfig.json` instead — but prefer the declaration file for a pure browser project to avoid Node-specific type collisions (e.g. `setTimeout` return type, `Buffer`, etc.).
158
+
146
159
  Compilation and type checking are intentionally separate — `npm start` and `npm run build` succeed regardless of type errors. Run `npx tsc --noEmit` during development to catch type issues without blocking the build.
147
160
 
148
161
  #### Exported types
@@ -176,13 +189,40 @@ The following types are exported from `anu-verzum` for use in consumer projects:
176
189
  #### Library development scripts
177
190
 
178
191
  ```bash
179
- npm run clean # Delete dist/ entirely
180
- npm run build # Clean, compile TypeScript sources to dist/, and emit .d.ts files
181
- npm run typecheck # Type-check without emitting any output
182
- npm run lint # Run ESLint on all source files
183
- npm run format # Format all source files with Prettier
192
+ npm run clean # Delete dist/ entirely
193
+ npm run build # Clean, compile TypeScript sources to dist/, and emit .d.ts files
194
+ npm run typecheck # Type-check without emitting any output
195
+ npm run lint # Run ESLint on all source files
196
+ npm run format # Format all source files with Prettier
197
+ npm test # Run the Anu Testing Library test suite with Jest
198
+ npm run test:watch # Run Jest in interactive watch mode
199
+ npm run test:coverage # Run Jest and generate a coverage report
200
+ ```
201
+
202
+ <br>
203
+ <hr>
204
+
205
+ <h2 id="testing">Testing</h2>
206
+
207
+ ANUVerzum ships a built-in testing library — **Anu Testing Library (ATL)** — importable as `anu-verzum/testing`. It mirrors the philosophy of Testing Library: query the DOM the way a user would (by role, text, label), fire events, and assert on real output — no component internals, no custom matchers.
208
+
209
+ ```typescript
210
+ import Anu from 'anu-verzum';
211
+ import { render, fireEvent } from 'anu-verzum/testing';
212
+
213
+ const { getByText, getByRole } = render(<Counter />);
214
+ fireEvent.click(getByRole('button'));
215
+ expect(getByText('Count: 1')).toBeDefined();
184
216
  ```
185
217
 
218
+ If TypeScript reports that `describe`, `test`, or `expect` are not found, your `tsconfig.json` likely has an explicit `"types"` list. Add `"jest"` to it — and `"node"` if tests reference `process.env`:
219
+
220
+ ```json
221
+ { "compilerOptions": { "types": ["node", "jest"] } }
222
+ ```
223
+
224
+ See **[USERS_MANUAL.md — Testing](./documentation/USERS_MANUAL.md#testing)** for the full API reference and usage guide.
225
+
186
226
  <br>
187
227
  <hr>
188
228
 
@@ -29,3 +29,6 @@ declare class AnulyticsProvider extends Component<AnulyticsProviderProps> {
29
29
  render(): AnuElement | AnuElement[] | null;
30
30
  }
31
31
  export default AnulyticsProvider;
32
+ export declare const __testing: {
33
+ reset(): void;
34
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.trackStateChange = exports.trackRouteChange = exports.trackEvent = exports.default = void 0;
6
+ exports.trackStateChange = exports.trackRouteChange = exports.trackEvent = exports.default = exports.__testing = void 0;
7
7
  var _Component = require("./Component");
8
8
  var _serverApi = _interopRequireDefault(require("../../server-api/server-api"));
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -15,30 +15,42 @@ const EventTypes = {
15
15
  PAGE_LEAVE: 'pageLeave'
16
16
  };
17
17
  const AnulyticsState = (() => {
18
- const initStart = new Date().getTime();
18
+ const startDate = new Date().getTime();
19
19
  let _anulyticsInstanceExist = false;
20
- const setAnulyticsInstanceExist = instanceExist => {
21
- _anulyticsInstanceExist = instanceExist;
22
- };
23
- const getAnulyticsInstanceExist = () => _anulyticsInstanceExist;
24
- const _anulytics = {
25
- startDate: initStart,
26
- events: [{
27
- [EventTypes.INITIALIZATION]: {
28
- eventType: window.location.pathname,
29
- timestamp: initStart,
30
- properties: {}
31
- }
32
- }],
33
- user: {}
20
+ let _analyticsUrl = '';
21
+ let _onSuccess = () => {};
22
+ let _onFail = () => {};
23
+ let _user = {};
24
+ const send = (event, extra) => {
25
+ _serverApi.default.post(_analyticsUrl, {
26
+ startDate,
27
+ user: _user,
28
+ ...event,
29
+ ...(extra || {})
30
+ }).then(({
31
+ response
32
+ }) => _onSuccess(response)).catch(({
33
+ status
34
+ }) => _onFail(status));
34
35
  };
35
36
  return {
36
- getAnulyticsInstanceExist,
37
- setAnulyticsInstanceExist,
38
- addEvent: (key, val) => {
39
- _anulytics.events.push({
37
+ getAnulyticsInstanceExist: () => _anulyticsInstanceExist,
38
+ setAnulyticsInstanceExist: instanceExist => {
39
+ _anulyticsInstanceExist = instanceExist;
40
+ },
41
+ setConfig: (url, onSuccess, onFail) => {
42
+ _analyticsUrl = url;
43
+ _onSuccess = onSuccess;
44
+ _onFail = onFail;
45
+ },
46
+ setUser: user => {
47
+ _user = user || {};
48
+ },
49
+ getStartDate: () => startDate,
50
+ sendEvent: (key, val, extra) => {
51
+ send({
40
52
  [key]: val
41
- });
53
+ }, extra);
42
54
  },
43
55
  trackEvent: ({
44
56
  type,
@@ -55,7 +67,7 @@ const AnulyticsState = (() => {
55
67
  const scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
56
68
  const scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
57
69
  const props = typeof rawProps === 'object' && !Array.isArray(rawProps) && rawProps !== null ? rawProps : null;
58
- const event = {
70
+ send({
59
71
  [EventTypes.USER_ACTION]: {
60
72
  eventType: type,
61
73
  timestamp: new Date().getTime(),
@@ -73,11 +85,10 @@ const AnulyticsState = (() => {
73
85
  props
74
86
  }
75
87
  }
76
- };
77
- _anulytics.events.push(event);
88
+ });
78
89
  },
79
90
  trackStateChange: (prevState, action, nextState) => {
80
- const event = {
91
+ send({
81
92
  [EventTypes.STATE_CHANGE]: {
82
93
  eventType: action['type'],
83
94
  timestamp: new Date().getTime(),
@@ -88,13 +99,8 @@ const AnulyticsState = (() => {
88
99
  nextState
89
100
  }
90
101
  }
91
- };
92
- _anulytics.events.push(event);
93
- },
94
- setUser: user => {
95
- _anulytics.user = user || {};
96
- },
97
- getAnulyticsData: () => _anulytics
102
+ });
103
+ }
98
104
  };
99
105
  })();
100
106
  const _isBot = !!(window.phantom || window._phantom || window.__nightmare || window.navigator.webdriver || window.Cypress);
@@ -122,26 +128,34 @@ exports.trackStateChange = trackStateChange;
122
128
  const trackRouteChange = path => {
123
129
  if (AnulyticsState.getAnulyticsInstanceExist()) {
124
130
  const url = path || window.location.pathname;
125
- const event = {
131
+ AnulyticsState.sendEvent(EventTypes.NAVIGATION, {
126
132
  eventType: url,
127
133
  timestamp: new Date().getTime(),
128
134
  properties: {}
129
- };
130
- AnulyticsState.addEvent(EventTypes.NAVIGATION, event);
135
+ });
131
136
  }
132
137
  };
133
138
  exports.trackRouteChange = trackRouteChange;
134
139
  class AnulyticsProvider extends _Component.Component {
135
140
  constructor(props) {
136
141
  super(props);
137
- AnulyticsState.setAnulyticsInstanceExist(true);
138
142
  this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
139
143
  }
140
144
  componentDidMount() {
141
145
  const {
142
- userData
146
+ analyticsUrl,
147
+ userData,
148
+ onSuccess,
149
+ onFail
143
150
  } = this.props;
151
+ AnulyticsState.setConfig(analyticsUrl, onSuccess, onFail);
144
152
  AnulyticsState.setUser(userData || null);
153
+ AnulyticsState.setAnulyticsInstanceExist(true);
154
+ AnulyticsState.sendEvent(EventTypes.INITIALIZATION, {
155
+ eventType: window.location.pathname,
156
+ timestamp: AnulyticsState.getStartDate(),
157
+ properties: {}
158
+ });
145
159
  document.addEventListener('visibilitychange', this.handleVisibilityChange, {
146
160
  passive: true
147
161
  });
@@ -151,23 +165,12 @@ class AnulyticsProvider extends _Component.Component {
151
165
  AnulyticsState.setAnulyticsInstanceExist(false);
152
166
  }
153
167
  handleVisibilityChange() {
154
- const {
155
- analyticsUrl,
156
- onSuccess,
157
- onFail
158
- } = this.props;
159
168
  if (_isBot) {
160
169
  return;
161
170
  }
162
171
  if (document.visibilityState === 'hidden') {
163
172
  const url = window.location.pathname;
164
- const event = {
165
- eventType: url,
166
- timestamp: new Date().getTime(),
167
- properties: {}
168
- };
169
173
  let ua;
170
- AnulyticsState.addEvent(EventTypes.PAGE_LEAVE, event);
171
174
  if (window.navigator.userAgentData) {
172
175
  const {
173
176
  brands,
@@ -186,21 +189,18 @@ class AnulyticsProvider extends _Component.Component {
186
189
  platform: ''
187
190
  };
188
191
  }
189
- const data = {
190
- ...AnulyticsState.getAnulyticsData(),
191
- endDate: new Date().getTime(),
192
+ AnulyticsState.sendEvent(EventTypes.PAGE_LEAVE, {
193
+ eventType: url,
194
+ timestamp: new Date().getTime(),
195
+ properties: {}
196
+ }, {
192
197
  system: {
193
198
  referrer: document.referrer || null,
194
199
  innerWidth: window.innerWidth,
195
200
  isMobileAppInstalled: _isInstalled(),
196
201
  userAgentData: ua
197
202
  }
198
- };
199
- _serverApi.default.post(analyticsUrl, data).then(({
200
- response
201
- }) => onSuccess(response)).catch(({
202
- status
203
- }) => onFail(status));
203
+ });
204
204
  } else {
205
205
  this.setState();
206
206
  }
@@ -218,4 +218,12 @@ class AnulyticsProvider extends _Component.Component {
218
218
  }
219
219
  }
220
220
  }
221
- var _default = exports.default = AnulyticsProvider;
221
+ var _default = exports.default = AnulyticsProvider;
222
+ const __testing = exports.__testing = {
223
+ reset() {
224
+ if (process.env.NODE_ENV !== 'test') {
225
+ return;
226
+ }
227
+ AnulyticsState.setAnulyticsInstanceExist(false);
228
+ }
229
+ };
@@ -47,3 +47,6 @@ declare const History: {
47
47
  getAllUrlParamNames: () => string[];
48
48
  };
49
49
  export default History;
50
+ export declare const __testing: {
51
+ reset(): void;
52
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.goTo = exports.default = void 0;
6
+ exports.goTo = exports.default = exports.__testing = void 0;
7
7
  var _elements = require("../elements");
8
8
  var _Component = require("./Component");
9
9
  var _AnulyticsProvider = require("./AnulyticsProvider");
@@ -210,4 +210,12 @@ const History = {
210
210
  getUrlParams,
211
211
  getAllUrlParamNames
212
212
  };
213
- var _default = exports.default = History;
213
+ var _default = exports.default = History;
214
+ const __testing = exports.__testing = {
215
+ reset() {
216
+ if (process.env.NODE_ENV !== 'test') {
217
+ return;
218
+ }
219
+ instances.length = 0;
220
+ }
221
+ };
@@ -21,3 +21,6 @@ declare const Intl: {
21
21
  Provider: ({ locale, messages, defaultLocale, children }: IntlProviderProps) => AnuElement | null;
22
22
  };
23
23
  export default Intl;
24
+ export declare const __testing: {
25
+ reset(): void;
26
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = void 0;
6
+ exports.default = exports.__testing = void 0;
7
7
  var _elements = require("../elements");
8
8
  var _Context = require("./Context");
9
9
  const _Intl = (0, _Context.createContext)({});
@@ -168,4 +168,15 @@ const Intl = {
168
168
  formatMessage,
169
169
  Provider: IntlProvider
170
170
  };
171
- var _default = exports.default = Intl;
171
+ var _default = exports.default = Intl;
172
+ const __testing = exports.__testing = {
173
+ reset() {
174
+ if (process.env.NODE_ENV !== 'test') {
175
+ return;
176
+ }
177
+ __messagesContext = {
178
+ locale: undefined,
179
+ messages: {}
180
+ };
181
+ }
182
+ };
@@ -12,7 +12,7 @@ export type Ref<T> = {
12
12
  };
13
13
  export type FunctionComponent<P extends Props = Props> = (props: P) => AnuElement<any, any> | AnuElement<any, any>[] | string | number | boolean | null | undefined;
14
14
  export type ComponentConstructor<P extends Props = Props> = new (props: P, context?: Record<string, any>) => any;
15
- export type ElementType = string | FunctionComponent | ComponentConstructor;
15
+ export type ElementType = string | FunctionComponent<any> | ComponentConstructor<any>;
16
16
  export type AnuElement<P = Props, T extends ElementType = ElementType> = {
17
17
  type: T;
18
18
  props: P;
@@ -2,3 +2,8 @@ import { AnuElement, Ref } from './elements';
2
2
  export declare const createRef: <T = any>() => Ref<T>;
3
3
  export declare const scheduleUpdate: (instance: any, partialState: Record<string, any>, partialStateCallback?: (prevState: any, prevProps: any) => any) => void;
4
4
  export declare const render: (elements: AnuElement | AnuElement[], containerDom: Element) => void;
5
+ export declare const unmountComponentAtNode: (containerDom: Element) => void;
6
+ export declare const __testing: {
7
+ flushSync(): void;
8
+ resetGlobals(): void;
9
+ };
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.scheduleUpdate = exports.render = exports.createRef = void 0;
6
+ exports.unmountComponentAtNode = exports.scheduleUpdate = exports.render = exports.createRef = exports.__testing = void 0;
7
7
  var _domUtils = require("./domUtils");
8
8
  const HOST_COMPONENT = 'host';
9
9
  const CLASS_COMPONENT = 'class';
@@ -446,4 +446,43 @@ const render = (elements, containerDom) => {
446
446
  });
447
447
  requestIdleCallback(performWork);
448
448
  };
449
- exports.render = render;
449
+ exports.render = render;
450
+ const unmountComponentAtNode = containerDom => {
451
+ if (!containerDom._rootContainerFiber) {
452
+ return;
453
+ }
454
+ updateQueue.push({
455
+ from: HOST_ROOT,
456
+ dom: containerDom,
457
+ newProps: {
458
+ children: []
459
+ }
460
+ });
461
+ requestIdleCallback(performWork);
462
+ };
463
+ exports.unmountComponentAtNode = unmountComponentAtNode;
464
+ const __testing = exports.__testing = {
465
+ flushSync() {
466
+ if (process.env.NODE_ENV !== 'test') {
467
+ return;
468
+ }
469
+ const syncDeadline = {
470
+ didTimeout: false,
471
+ timeRemaining: () => 999
472
+ };
473
+ while (updateQueue.length > 0 || nextUnitOfWork != null) {
474
+ workLoop(syncDeadline);
475
+ }
476
+ nextUnitOfWork = null;
477
+ pendingCommit = null;
478
+ },
479
+ resetGlobals() {
480
+ if (process.env.NODE_ENV !== 'test') {
481
+ return;
482
+ }
483
+ updateQueue.length = 0;
484
+ componentLifecyclesQueue.length = 0;
485
+ nextUnitOfWork = null;
486
+ pendingCommit = null;
487
+ }
488
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+
3
+ var _index = _interopRequireWildcard(require("../../index"));
4
+ var _index2 = require("../index");
5
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
6
+ class Counter extends _index.Component {
7
+ state = {
8
+ count: 0
9
+ };
10
+ render() {
11
+ return _index.default.createElement('div', {}, _index.default.createElement('p', {}, `Count: ${this.state.count}`), _index.default.createElement('button', {
12
+ onClick: () => this.setState({
13
+ count: this.state.count + 1
14
+ })
15
+ }, 'Increment'));
16
+ }
17
+ }
18
+ const Greeting = ({
19
+ name
20
+ }) => _index.default.createElement('h1', {}, `Hello, ${name}!`);
21
+ describe('render', () => {
22
+ test('renders a function component', () => {
23
+ const {
24
+ getByText
25
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
26
+ name: 'World'
27
+ }));
28
+ expect(getByText('Hello, World!')).toBeDefined();
29
+ });
30
+ test('renders a class component', () => {
31
+ const {
32
+ getByText
33
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
34
+ expect(getByText('Count: 0')).toBeDefined();
35
+ });
36
+ test('returns a container attached to document.body', () => {
37
+ const {
38
+ container
39
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
40
+ name: 'test'
41
+ }));
42
+ expect(document.body.contains(container)).toBe(true);
43
+ });
44
+ });
45
+ describe('queries', () => {
46
+ test('getByRole finds button', () => {
47
+ const {
48
+ getByRole
49
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
50
+ const btn = getByRole('button');
51
+ expect(btn).toBeDefined();
52
+ expect(btn.textContent).toBe('Increment');
53
+ });
54
+ test('getByRole with name option', () => {
55
+ const {
56
+ getByRole
57
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
58
+ const btn = getByRole('button', {
59
+ name: 'Increment'
60
+ });
61
+ expect(btn).toBeDefined();
62
+ });
63
+ test('queryByText returns null when not found', () => {
64
+ const {
65
+ queryByText
66
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
67
+ name: 'test'
68
+ }));
69
+ expect(queryByText('Not here')).toBeNull();
70
+ });
71
+ test('getByText throws when not found', () => {
72
+ const {
73
+ getByText
74
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
75
+ name: 'test'
76
+ }));
77
+ expect(() => getByText('Not here')).toThrow();
78
+ });
79
+ test('findByText resolves asynchronously', async () => {
80
+ const {
81
+ findByText
82
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
83
+ name: 'World'
84
+ }));
85
+ const el = await findByText('Hello, World!');
86
+ expect(el).toBeDefined();
87
+ });
88
+ });
89
+ describe('fireEvent', () => {
90
+ test('click triggers onClick and re-renders', () => {
91
+ const {
92
+ getByText,
93
+ getByRole
94
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
95
+ expect(getByText('Count: 0')).toBeDefined();
96
+ _index2.fireEvent.click(getByRole('button'));
97
+ expect(getByText('Count: 1')).toBeDefined();
98
+ });
99
+ test('multiple clicks accumulate', () => {
100
+ const {
101
+ getByText,
102
+ getByRole
103
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
104
+ _index2.fireEvent.click(getByRole('button'));
105
+ _index2.fireEvent.click(getByRole('button'));
106
+ _index2.fireEvent.click(getByRole('button'));
107
+ expect(getByText('Count: 3')).toBeDefined();
108
+ });
109
+ });
110
+ describe('userEvent', () => {
111
+ test('click fires full mouse event sequence', () => {
112
+ const {
113
+ getByText,
114
+ getByRole
115
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
116
+ _index2.userEvent.click(getByRole('button'));
117
+ expect(getByText('Count: 1')).toBeDefined();
118
+ });
119
+ });
120
+ describe('rerender', () => {
121
+ test('updates the component with new props', () => {
122
+ const {
123
+ getByText,
124
+ rerender
125
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
126
+ name: 'Alice'
127
+ }));
128
+ expect(getByText('Hello, Alice!')).toBeDefined();
129
+ rerender(_index.default.createElement(Greeting, {
130
+ name: 'Bob'
131
+ }));
132
+ expect(getByText('Hello, Bob!')).toBeDefined();
133
+ });
134
+ });
135
+ describe('waitFor', () => {
136
+ test('polls until assertion passes', async () => {
137
+ const {
138
+ getByText
139
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
140
+ name: 'World'
141
+ }));
142
+ await (0, _index2.waitFor)(() => {
143
+ expect(getByText('Hello, World!')).toBeDefined();
144
+ });
145
+ });
146
+ test('rejects when assertion never passes', async () => {
147
+ const {
148
+ queryByText
149
+ } = (0, _index2.render)(_index.default.createElement(Greeting, {
150
+ name: 'World'
151
+ }));
152
+ await expect((0, _index2.waitFor)(() => {
153
+ expect(queryByText('MISSING')).not.toBeNull();
154
+ }, {
155
+ timeout: 100,
156
+ interval: 20
157
+ })).rejects.toThrow();
158
+ });
159
+ });
160
+ describe('act', () => {
161
+ test('flushes state updates synchronously', () => {
162
+ const {
163
+ getByText,
164
+ getByRole
165
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
166
+ (0, _index2.act)(() => {
167
+ _index2.fireEvent.click(getByRole('button'));
168
+ });
169
+ expect(getByText('Count: 1')).toBeDefined();
170
+ });
171
+ test('wraps async callbacks', async () => {
172
+ const {
173
+ getByText
174
+ } = (0, _index2.render)(_index.default.createElement(Counter, {}));
175
+ await (0, _index2.act)(async () => {
176
+ await Promise.resolve();
177
+ });
178
+ expect(getByText('Count: 0')).toBeDefined();
179
+ });
180
+ });
@@ -0,0 +1,4 @@
1
+ export declare const installSyncScheduler: () => void;
2
+ export declare const uninstallSyncScheduler: () => void;
3
+ export declare const flushEffects: () => void;
4
+ export declare const act: (callback: () => void | Promise<void>) => void | Promise<void>;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.uninstallSyncScheduler = exports.installSyncScheduler = exports.flushEffects = exports.act = void 0;
7
+ var _reconciler = require("../core/reconciler");
8
+ const FAKE_DEADLINE = {
9
+ didTimeout: false,
10
+ timeRemaining: () => 999
11
+ };
12
+ let _installed = false;
13
+ const installSyncScheduler = () => {
14
+ if (_installed) {
15
+ return;
16
+ }
17
+ _installed = true;
18
+ window.requestIdleCallback = cb => {
19
+ cb(FAKE_DEADLINE);
20
+ return 0;
21
+ };
22
+ window.cancelIdleCallback = () => {};
23
+ };
24
+ exports.installSyncScheduler = installSyncScheduler;
25
+ const uninstallSyncScheduler = () => {
26
+ _installed = false;
27
+ };
28
+ exports.uninstallSyncScheduler = uninstallSyncScheduler;
29
+ const flushEffects = () => {
30
+ _reconciler.__testing.flushSync();
31
+ };
32
+ exports.flushEffects = flushEffects;
33
+ const act = callback => {
34
+ const result = callback();
35
+ if (result && typeof result.then === 'function') {
36
+ return result.then(() => {
37
+ _reconciler.__testing.flushSync();
38
+ });
39
+ }
40
+ _reconciler.__testing.flushSync();
41
+ };
42
+ exports.act = act;
@@ -0,0 +1,3 @@
1
+ export declare const registerContainer: (container: Element) => void;
2
+ export declare const cleanup: () => void;
3
+ export declare const setupAutoCleanup: () => void;