@servicetitan/docs-anvil-uikit-contrib 25.5.0 → 25.7.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.
@@ -0,0 +1,250 @@
1
+ ---
2
+ title: API
3
+ ---
4
+
5
+ ## MockApiCalls
6
+
7
+ When it isn't practical to [mock the API service classes](frontend/unit-testing#migrating-from-autogenerated-api-mocks), use `MockApiCalls` to configure responses for API endpoints.
8
+
9
+ ```ts
10
+ describe('[admin] AppRoutes', () => {
11
+ // Create helper to mock API calls
12
+ const apiResponses = MockApiCalls();
13
+
14
+ beforeEach(() => {
15
+ apiResponses.reset();
16
+ // Configure /Admin/isGoEnvironment to return false
17
+ apiResponses.add(/Admin\/isGoEnvironment/, { body: () => false });
18
+ });
19
+
20
+ describe('when user is not authorized', () => {
21
+ // Configure /Admin/GetClientData to return 401 Unauthorized
22
+ beforeEach(() => apiResponses.add(/Admin\/GetClientData/, { status: 401 }));
23
+ });
24
+ });
25
+ ```
26
+
27
+ It exposes the following methods:
28
+
29
+ - [add](#add): Configure an API response
30
+ - [replace](#replace): Replace a previously configured response
31
+ - [reset](#reset): Remove all previously configured responses
32
+
33
+ ### add
34
+
35
+ Configure API response.
36
+
37
+ ```ts
38
+ add(
39
+ path: RegExp,
40
+ response: {
41
+ body?: (url: string) => any,
42
+ status?: number,
43
+ }
44
+ )
45
+ ```
46
+
47
+ - `path`: regular expression to match against GET request URLs. If a URL matches more than one path, the first one wins.
48
+ - `body`: function that returns the API response. If omitted, or if it returns `undefined`, the mock returns an empty object (`{}`).
49
+ - `status`: HTTP status code for API response. Defaults to 200.
50
+
51
+ ```ts
52
+ // Configure GET /desktop/version.json to return test clientVersion
53
+ apiResponses.add(/desktop\/version\.json/, { body: () => ({ clientVersion }) });
54
+
55
+ // Configure GET /Admin/GetClientData to return 401 status
56
+ apiResponses.add(/Admin\/GetClientData/, { status: 401 });
57
+ ```
58
+
59
+ **Note:** `MockApiCalls` passes the request URL to the `body` function so that it can customize the return value based on query params or other attributes. This also facilitates logging API calls for debugging purposes.
60
+
61
+ ```ts
62
+ // Log all API calls
63
+ apiResponses.add(/.*/, { body: (url: string) => console.log(`GET ${url}`) });
64
+ ```
65
+
66
+ ### replace
67
+
68
+ Replace a previously configured API response.
69
+
70
+ ```ts
71
+ replace(
72
+ path: RegExp,
73
+ response: {
74
+ body?: (url: string) => any,
75
+ status?: number,
76
+ }
77
+ )
78
+ ```
79
+
80
+ - `path`: the exact regular expression to replace
81
+ - `response`: see [add](#add)
82
+
83
+ The `path` must exactly match a previously configured API response. Otherwise, the function does nothing.
84
+
85
+ ```ts
86
+ // Change GET /Admin/GetClientData to return 403 status
87
+ apiResponses.replace(/Admin\/GetClientData/, { status: 403 });
88
+ ```
89
+
90
+ ### reset
91
+
92
+ Remove all previously configured responses.
93
+
94
+ ```ts
95
+ apiResponses.reset();
96
+ ```
97
+
98
+ ## mockComponent
99
+
100
+ Use `mockComponent` with `jest.mock` to replace a React component with a stub that renders a placeholder instead of the actual component. Test cans then use the [`toContainComponent`](./matchers/#tocontaincomponent) matcher to confirm the component was rendered correctly.
101
+
102
+ ```ts
103
+ mockComponent(
104
+ name: string,
105
+ options?: {
106
+ renderChildren?: boolean = false,
107
+ renderProps?: boolean = true,
108
+ forwardRef?: boolean = false,
109
+ }
110
+ )
111
+ ```
112
+
113
+ - `renderChildren`: Set to `true` to render both the component and it's children Defaults to `false`.
114
+ - `renderProps`: Set to `true` to render the component's props. Defaults to `true`.
115
+ - `forwardRef`: Set to `true` when mocking a component that is wrapped within `React.forwardRef`. Defaults to `false`.
116
+
117
+ ```ts
118
+ // Replace <BillingNewAdmin> component with placeholder
119
+ jest.mock('../modules/billing/components/billing-new-admin', () =>
120
+ mockComponent('BillingNewAdmin')
121
+ );
122
+
123
+ // Check that placeholder was rendered
124
+ expect(screen).toContainComponent('BillingNewAdmin');
125
+ ```
126
+
127
+ ## mockLocation
128
+
129
+ Use `mockLocation` to change or mock `window.location`.
130
+
131
+ ```ts
132
+ mockLocation(
133
+ newLocation: string | {
134
+ origin?: string,
135
+ pathname?: string,
136
+ }
137
+ )
138
+ ```
139
+
140
+ When called with a string, it sets `location.pathname`.
141
+
142
+ Use the object signature to change either or both `location.origin` and `location.pathname`.
143
+
144
+ - `origin`: new `location.origin`. Defaults to preserving previous value.
145
+ - `pathname`: new `location.pathname`. Defaults to preserving previous value.
146
+
147
+ To mock `window.location` without changing the value, pass an empty object.
148
+
149
+ ```ts
150
+ // Change location.pathname to "/login" (location.origin remains unchanged)
151
+ mockLocation('/login');
152
+
153
+ // Change location.origin to "https//jest.st.dev" (location.pathname remains unchanged)
154
+ mockLocation({ origin: 'https://jest.st.dev' });
155
+
156
+ // Enable spying on window.location
157
+ mockLocation({});
158
+ // ...
159
+ expect(window.location.reload).toHaveBeenCalled();
160
+ ```
161
+
162
+ ## mockStyles
163
+
164
+ Use `mockStyles` with `jest.mock` to mock imported CSS styles.
165
+
166
+ ```ts
167
+ // Mock CSS styles
168
+ jest.mock('../header.module.less', () => mockStyles());
169
+ import * as Styles from '../header.module.less';
170
+
171
+ // Check that style was rendered
172
+ expect(screen).toContainComponent('Header', { className: Styles.header });
173
+ ```
174
+
175
+ ## mockWithDefaultLayout
176
+
177
+ Use `mockWithDefaultLayout` with `jest.mock` to mock `withDefaultLayout`.
178
+
179
+ ```ts
180
+ jest.mock('../modules/common/components/default-layout', () => ({
181
+ withDefaultLayout: mockWithDefaultLayout(),
182
+ ...mockComponent('DefaultLayout'),
183
+ }));
184
+ ```
185
+
186
+ ## renderHookWithProvider
187
+
188
+ Use `renderHookWithProvider` to [render a hook](https://react-hooks-testing-library.com/reference/api#renderhook) within a `<Provider>` wrapper with the specified singletons.
189
+
190
+ ```ts
191
+ renderHookWithProvider<TProps, TResult>(
192
+ singletons: ProviderProps['singletons'],
193
+ callback: (props: TProps) => TResult,
194
+ options?: RenderHookOptions<TProps>
195
+ )
196
+ ```
197
+
198
+ ```ts
199
+ const subject = () => {
200
+ const { result } = renderHookWithProvider(
201
+ [
202
+ { provide: MainAppStore, useValue: mainAppStore },
203
+ { provide: AppUserStore, useValue: appUserStore },
204
+ { provide: FeatureStore, useValue: featureStore },
205
+ ],
206
+ () => useTenantName()
207
+ );
208
+ return result.current;
209
+ };
210
+ ```
211
+
212
+ ## setupApp
213
+
214
+ Use `setupApp` to mock the Monolith's global `window.App` state. It restores `window.App` to a clean state before applying the specified values.
215
+
216
+ To preserve the previous state, spread `window.App` into the argument.
217
+
218
+ ```ts
219
+ setupApp({
220
+ [key: string]: Record<string, any> | undefined;
221
+ AppUser?: Record<string, any>;
222
+ Data?: Record<string, any>;
223
+ Enum?: Record<string, any>;
224
+ Features?: Record<string, any>;
225
+ Permissions?: Record<string, any>;
226
+ Services?: Record<string, any>;
227
+ UserNotifications?: Record<string, any>;
228
+ Utils?: Record<string, any>;
229
+ })
230
+ ```
231
+
232
+ ```ts
233
+ // Mock window.App.IsSandboxedProductionInstance and window.App.IsStageInstance
234
+ // (resets other attributes to initial state)
235
+ setupApp({ IsSandboxedProductionInstance: () => false, IsStageInstance: () => false });
236
+
237
+ // Mock window.App.Features (resets other attributes to initial state)
238
+ beforeEach(() => {
239
+ setupApp({
240
+ Features: {
241
+ EnableDispatchCenter: true,
242
+ DispatchCenterApiUrl: 'DispatchCenterApiUrl',
243
+ DispatchCenterMicroFrontendUrl: 'DispatchCenterMicroFrontendUrl',
244
+ },
245
+ });
246
+ });
247
+
248
+ // Mock window.App.LogService and keep previous state of other attributes
249
+ setupApp({ ...window.App, LogService: { error: jest.fn() } });
250
+ ```
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Queries
3
+ ---
4
+
5
+ Testing Library includes custom [queries](https://testing-library.com/docs/queries/about) that find elements with ServiceTitan specific attributes.
6
+
7
+ ## Usage
8
+
9
+ To use custom queries, import them from Testing Library and merge them with the default set of queries attached to rendered components.
10
+
11
+ ```ts
12
+ import { queries, render } from '@testing-library/react'; // Import default queries
13
+ import { queries as customQueries } from '@servicetitan/testing-library'; // Import custom queries
14
+
15
+ // ...
16
+
17
+ const subject = () => {
18
+ return render(
19
+ <MyComponent />,
20
+ // Merge custom queries with defaults
21
+ { queries: { ...queries, ...customQueries } }
22
+ );
23
+ };
24
+ ```
25
+
26
+ ## ByAnvilComponentName
27
+
28
+ > getByAnvilComponentName, queryByAnvilComponentName, getAllByAnvilComponentName, queryAllByAnvilComponentName, findByAnvilComponentName, findAllByAnvilComponentName
29
+
30
+ Searches for all elements that have a node with the `data-anvil-component` attribute and content matching the given `Matcher`.
31
+
32
+ **Use with caution.** Using Anvil attributes does not resemble how users see the software and should be avoided. Use this only when queries for user-visible attributes (e.g., **ByRole**, **ByText**) don't work.
33
+
34
+ ```ts
35
+ ByAnvilComponentName(
36
+ container: HTMLElement,
37
+ id: Matcher,
38
+ options?: MatcherOptions,
39
+ )
40
+ ```
41
+
42
+ ```ts
43
+ // Custom queries are bound to render result
44
+ const { getByAnvilComponentName } = subject();
45
+
46
+ // Check that subject() rendered Anvil icon with specified class
47
+ expect(getByAnvilComponentName('Icon')).toHaveClass('foo');
48
+ ```
@@ -0,0 +1,117 @@
1
+ ---
2
+ title: Matchers
3
+ ---
4
+
5
+ ## toBeWithinRange
6
+
7
+ Checks whether a value equals another value, plus or minus the specified delta.
8
+
9
+ ```ts
10
+ toBeWithinRange(
11
+ value: number,
12
+ delta: number
13
+ )
14
+ ```
15
+
16
+ ```ts
17
+ // Check whether actual equals expected ± 0.01
18
+ expect(actual).toBeWithinRange(expect, 0.01);
19
+ ```
20
+
21
+ ## toContainAnvilComponent
22
+
23
+ Checks whether an element contains an Anvil component (see [ByAnvilComponentName](./queries/#byanvilcomponentname)) with the specified name.
24
+
25
+ **Use with caution.** Using Anvil attributes does not resemble how users see the software and should be avoided. Use this only when queries for user-visible attributes (e.g., **ByRole**, **ByText**) don't work.
26
+
27
+ ```ts
28
+ toContainAnvilComponent(
29
+ name: string,
30
+ options?: SelectorMatcherOptions
31
+ )
32
+ ```
33
+
34
+ ```ts
35
+ // Check that Anvil Icon component was rendered
36
+ expect(screen).toContainAnvilComponent('Icon');
37
+ ```
38
+
39
+ ## toContainClassName
40
+
41
+ Checks whether an element contains another element with the specified class.
42
+
43
+ ```ts
44
+ toContainClassName(
45
+ className: string
46
+ )
47
+ ```
48
+
49
+ ```ts
50
+ // Check that element with CSS class .in-partner-portal was rendered
51
+ expect(screen).toContainClassName('in-partner-portal');
52
+ ```
53
+
54
+ ## toContainComponent
55
+
56
+ Checks whether an element contains a [mocked component](./api/#mockcomponent) with the specified name and React props.
57
+
58
+ ```ts
59
+ toContainComponent(
60
+ name: string,
61
+ props?: Record<string, any>,
62
+ options?: {
63
+ exact?: boolean = true,
64
+ }
65
+ )
66
+ ```
67
+
68
+ - `name`: the mocked component's name
69
+ - `props`: props to check were passed to the component
70
+ - `exact`: controls whether only specified the props are allowed. Defaults to `true`.
71
+
72
+ ```ts
73
+ // Check that <MarketingModule /> was rendered (with no props)
74
+ expect(screen).toContainComponent('MarketingModule');
75
+
76
+ // Check that <Redirect to="/login" /> was rendered
77
+ expect(screen).toContainComponent('Redirect', { to: '/login' });
78
+ ```
79
+
80
+ To allow partial matches, set `exact` to `false`. For example,
81
+
82
+ ```ts
83
+ // Check that exactly <Tag color="critical" /> was render
84
+ expect(screen).toContainComponent('Tag', { color: 'critical' });
85
+
86
+ // Check that <Tag /> was rendered with at least color="critical", and possibly other options
87
+ expect(screen).toContainComponent('Tag', { color: 'critical' }, { exact: false });
88
+
89
+ // Check that <Tag /> was rendered with or without any options
90
+ expect(screen).toContainComponent('Tag', {}, { exact: false });
91
+ ```
92
+
93
+ ## toContainText
94
+
95
+ Checks whether an element contains the specified text.
96
+
97
+ ```ts
98
+ toContainText(
99
+ text?: string | RegExp,
100
+ options?: SelectorMatcherOptions,
101
+ )
102
+ ```
103
+
104
+ ```ts
105
+ // Check that screen contains instructions to download Chrome
106
+ expect(screen).toContainText(/download.*Chrome/i);
107
+ ```
108
+
109
+ To check whether the element contains any text at all, omit the `text` argument. For example,
110
+
111
+ ```ts
112
+ // Check that any text was rendered
113
+ expect(screen).toContainText();
114
+
115
+ // Check that no text was rendered
116
+ expect(screen).not.toContainText();
117
+ ```
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: Testing Library
3
+ ---
4
+
5
+ `@servicetitan/testing-library` is a light-weight solution for testing ServiceTitan components that builds on top of [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro/) and encourages testing best practices.
6
+
7
+ ## Usage
8
+
9
+ This project should be installed as one of your project's `devDependencies`:
10
+
11
+ ```bash
12
+ npm install --save-dev @servicetitan/testing-library
13
+ ```
14
+
15
+ This library has `peerDependencies` for:
16
+
17
+ - `@servicetitan/react-ioc`: >=22.0.0
18
+ - `@testing-library/react`: ^12.0.0
19
+ - `@testing-library/react-hooks`: ^7.0.0
20
+ - `react`: >=17.0.0
21
+
22
+ **Note:** This library is not compatible with React Testing Library versions 13+.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/docs-anvil-uikit-contrib",
3
- "version": "25.5.0",
3
+ "version": "25.7.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,5 +16,5 @@
16
16
  "cli": {
17
17
  "webpack": false
18
18
  },
19
- "gitHead": "66af05bc2547584bc9eb034dfcd692ef256ba379"
19
+ "gitHead": "3e0aaed77a4a1cbdc7675d2f47bcc8f144cd16c3"
20
20
  }