@chatlychat/react-native 0.1.1 → 0.1.2
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.
- package/README.md +1 -1
- package/dist/client/RealtimeClient.d.ts.map +1 -1
- package/dist/client/RealtimeClient.js +3 -1
- package/dist/client/RealtimeClient.js.map +1 -1
- package/dist/client/RestClient.d.ts.map +1 -1
- package/dist/client/RestClient.js +16 -1
- package/dist/client/RestClient.js.map +1 -1
- package/dist/client/types.d.ts +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/useChatly.d.ts.map +1 -1
- package/dist/useChatly.js.map +1 -1
- package/package.json +12 -3
- package/dist/blocks/money.parity.spec.d.ts +0 -2
- package/dist/blocks/money.parity.spec.d.ts.map +0 -1
- package/dist/blocks/money.parity.spec.js +0 -87
- package/dist/blocks/money.parity.spec.js.map +0 -1
- package/dist/blocks/read-envelope.parity.spec.d.ts +0 -2
- package/dist/blocks/read-envelope.parity.spec.d.ts.map +0 -1
- package/dist/blocks/read-envelope.parity.spec.js +0 -146
- package/dist/blocks/read-envelope.parity.spec.js.map +0 -1
- package/dist/client/RealtimeClient.spec.d.ts +0 -2
- package/dist/client/RealtimeClient.spec.d.ts.map +0 -1
- package/dist/client/RealtimeClient.spec.js +0 -123
- package/dist/client/RealtimeClient.spec.js.map +0 -1
- package/dist/client/RestClient.spec.d.ts +0 -2
- package/dist/client/RestClient.spec.d.ts.map +0 -1
- package/dist/client/RestClient.spec.js +0 -267
- package/dist/client/RestClient.spec.js.map +0 -1
- package/dist/components/MessageBubble.spec.d.ts +0 -2
- package/dist/components/MessageBubble.spec.d.ts.map +0 -1
- package/dist/components/MessageBubble.spec.js +0 -207
- package/dist/components/MessageBubble.spec.js.map +0 -1
- package/dist/components/MessageCanvas.spec.d.ts +0 -2
- package/dist/components/MessageCanvas.spec.d.ts.map +0 -1
- package/dist/components/MessageCanvas.spec.js +0 -142
- package/dist/components/MessageCanvas.spec.js.map +0 -1
- package/dist/test/react-native-dom.d.ts +0 -97
- package/dist/test/react-native-dom.d.ts.map +0 -1
- package/dist/test/react-native-dom.js +0 -155
- package/dist/test/react-native-dom.js.map +0 -1
- package/dist/test/react-native-dom.spec.d.ts +0 -2
- package/dist/test/react-native-dom.spec.d.ts.map +0 -1
- package/dist/test/react-native-dom.spec.js +0 -83
- package/dist/test/react-native-dom.spec.js.map +0 -1
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* The Tier 2 canvas on a native screen.
|
|
4
|
-
*
|
|
5
|
-
* ── The two properties that are easy to get wrong ──────────────────────
|
|
6
|
-
*
|
|
7
|
-
* The canvas on screen must be REPLACED by whatever the submission returns.
|
|
8
|
-
* A refused submission comes back as the same canvas with messages on it and
|
|
9
|
-
* a changed total comes back as a FRESH step rather than a charge — so a
|
|
10
|
-
* renderer that kept rendering its prop would show the shopper the old price
|
|
11
|
-
* beside the new errors, and would show a stale "Confirm · $48.20" after the
|
|
12
|
-
* server had already quoted $51.
|
|
13
|
-
*
|
|
14
|
-
* And a `hostEvent` on the answer must reach the APP. That is how 3-D Secure
|
|
15
|
-
* works on a phone: the step says "now run this", the app runs it natively.
|
|
16
|
-
* A renderer that swallowed it would leave a payment half-done with nothing
|
|
17
|
-
* on screen to say so.
|
|
18
|
-
*/
|
|
19
|
-
import { cleanup, render, screen, waitFor } from '@testing-library/react';
|
|
20
|
-
import userEvent from '@testing-library/user-event';
|
|
21
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
22
|
-
import { ChatlyContext } from '../ChatlyProvider';
|
|
23
|
-
import { MessageCanvas } from './MessageCanvas';
|
|
24
|
-
import { defaultTheme } from '../theme/defaultTheme';
|
|
25
|
-
afterEach(cleanup);
|
|
26
|
-
const CONV = 'conv-1';
|
|
27
|
-
const CANVAS = {
|
|
28
|
-
v: 1,
|
|
29
|
-
canvasId: 'buy-nd10',
|
|
30
|
-
title: 'Confirm purchase · $48.20',
|
|
31
|
-
fields: [
|
|
32
|
-
{ type: 'text', name: 'note', label: 'Gift note', placeholder: 'Optional' },
|
|
33
|
-
{ type: 'checkbox', name: 'gift', label: 'Send as a gift' },
|
|
34
|
-
],
|
|
35
|
-
actions: [
|
|
36
|
-
{ id: 'confirm', kind: 'submit', label: 'Buy now', style: 'primary' },
|
|
37
|
-
{ id: 'cancel', kind: 'cancel', label: 'Not now' },
|
|
38
|
-
],
|
|
39
|
-
state: 'quote:4820',
|
|
40
|
-
};
|
|
41
|
-
function harness(opts) {
|
|
42
|
-
const submitCanvas = vi.fn(() => opts.reject === true
|
|
43
|
-
? Promise.reject(new Error('offline'))
|
|
44
|
-
: Promise.resolve(opts.answer ?? { canvas: CANVAS, errors: {} }));
|
|
45
|
-
const value = {
|
|
46
|
-
theme: defaultTheme,
|
|
47
|
-
rest: { submitCanvas },
|
|
48
|
-
...(opts.onAction === undefined ? {} : { onAction: opts.onAction }),
|
|
49
|
-
};
|
|
50
|
-
render(_jsx(ChatlyContext.Provider, { value: value, children: _jsx(MessageCanvas, { canvas: CANVAS, conversationId: CONV, messageId: "m1" }) }));
|
|
51
|
-
return { submitCanvas };
|
|
52
|
-
}
|
|
53
|
-
describe('MessageCanvas', () => {
|
|
54
|
-
it('renders the title, the fields and the actions', () => {
|
|
55
|
-
harness({});
|
|
56
|
-
expect(screen.getByText('Confirm purchase · $48.20')).toBeDefined();
|
|
57
|
-
expect(screen.getByLabelText('Gift note')).toBeDefined();
|
|
58
|
-
expect(screen.getByRole('button', { name: 'Buy now' })).toBeDefined();
|
|
59
|
-
});
|
|
60
|
-
it('submits the typed values, with a checkbox as a boolean', async () => {
|
|
61
|
-
const h = harness({});
|
|
62
|
-
await userEvent.type(screen.getByLabelText('Gift note'), 'happy birthday');
|
|
63
|
-
await userEvent.click(screen.getByRole('button', { name: 'Send as a gift' }));
|
|
64
|
-
await userEvent.click(screen.getByRole('button', { name: 'Buy now' }));
|
|
65
|
-
await waitFor(() => {
|
|
66
|
-
expect(h.submitCanvas).toHaveBeenCalledWith(CONV, {
|
|
67
|
-
messageId: 'm1',
|
|
68
|
-
actionId: 'confirm',
|
|
69
|
-
values: { note: 'happy birthday', gift: true },
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
it('REPLACES the step with what the server returned', async () => {
|
|
74
|
-
// A changed total comes back as a fresh step rather than a charge. The
|
|
75
|
-
// old title must be gone, or the shopper confirms a price that moved.
|
|
76
|
-
const next = {
|
|
77
|
-
canvas: { ...CANVAS, title: 'Price changed · $51.00' },
|
|
78
|
-
errors: {},
|
|
79
|
-
};
|
|
80
|
-
harness({ answer: next });
|
|
81
|
-
await userEvent.click(screen.getByRole('button', { name: 'Buy now' }));
|
|
82
|
-
await waitFor(() => {
|
|
83
|
-
expect(screen.getByText('Price changed · $51.00')).toBeDefined();
|
|
84
|
-
});
|
|
85
|
-
expect(screen.queryByText('Confirm purchase · $48.20')).toBeNull();
|
|
86
|
-
});
|
|
87
|
-
it('shows a per-field error from a refused submission', async () => {
|
|
88
|
-
harness({ answer: { canvas: CANVAS, errors: { note: 'Too long' } } });
|
|
89
|
-
await userEvent.click(screen.getByRole('button', { name: 'Buy now' }));
|
|
90
|
-
await waitFor(() => {
|
|
91
|
-
expect(screen.getByText('Too long')).toBeDefined();
|
|
92
|
-
});
|
|
93
|
-
// The form is still usable: the field is still there to fix.
|
|
94
|
-
expect(screen.getByLabelText('Gift note')).toBeDefined();
|
|
95
|
-
});
|
|
96
|
-
it('hands a hostEvent on the answer to the APP', async () => {
|
|
97
|
-
// 3-D Secure. The step asks; the app runs it natively.
|
|
98
|
-
const onAction = vi.fn();
|
|
99
|
-
harness({
|
|
100
|
-
answer: {
|
|
101
|
-
canvas: CANVAS,
|
|
102
|
-
errors: {},
|
|
103
|
-
hostEvent: { event: 'confirm_payment', data: { pi: 'pi_1' } },
|
|
104
|
-
},
|
|
105
|
-
onAction,
|
|
106
|
-
});
|
|
107
|
-
await userEvent.click(screen.getByRole('button', { name: 'Buy now' }));
|
|
108
|
-
await waitFor(() => {
|
|
109
|
-
expect(onAction).toHaveBeenCalledWith({
|
|
110
|
-
event: 'confirm_payment',
|
|
111
|
-
data: { pi: 'pi_1' },
|
|
112
|
-
conversationId: CONV,
|
|
113
|
-
messageId: 'm1',
|
|
114
|
-
actionId: 'confirm',
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
it('leaves the form usable when the submission never lands', async () => {
|
|
119
|
-
harness({ reject: true });
|
|
120
|
-
await userEvent.click(screen.getByRole('button', { name: 'Buy now' }));
|
|
121
|
-
await waitFor(() => {
|
|
122
|
-
expect(screen.getByText('That did not go through. Try again.')).toBeDefined();
|
|
123
|
-
});
|
|
124
|
-
// Re-enabled, not stuck on the in-flight state: the shopper can retry and
|
|
125
|
-
// the server is the only thing that can settle the price.
|
|
126
|
-
expect(screen.getByRole('button', { name: 'Buy now' }).hasAttribute('disabled')).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
it('renders nothing for a canvas with no canvasId', () => {
|
|
129
|
-
// A form with nowhere to post is worse than no form.
|
|
130
|
-
const value = { theme: defaultTheme, rest: {} };
|
|
131
|
-
const { container } = render(_jsx(ChatlyContext.Provider, { value: value, children: _jsx(MessageCanvas, { canvas: { v: 1, fields: [] }, conversationId: CONV, messageId: "m1" }) }));
|
|
132
|
-
expect(container.textContent).toBe('');
|
|
133
|
-
});
|
|
134
|
-
it('explains a Tier 3 app rather than opening a WebView', () => {
|
|
135
|
-
// No sandbox attribute exists in React Native, and a WebView is the thing
|
|
136
|
-
// a native SDK exists to avoid.
|
|
137
|
-
const value = { theme: defaultTheme, rest: {} };
|
|
138
|
-
render(_jsx(ChatlyContext.Provider, { value: value, children: _jsx(MessageCanvas, { canvas: { v: 1, canvasId: 'c1', app: { url: 'https://x.test', title: 'Checkout' } }, conversationId: CONV, messageId: "m1" }) }));
|
|
139
|
-
expect(screen.getByText('Checkout opens outside the chat.')).toBeDefined();
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
//# sourceMappingURL=MessageCanvas.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MessageCanvas.spec.js","sourceRoot":"","sources":["../../src/components/MessageCanvas.spec.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A DOM stand-in for the React Native primitives THIS package uses, so its
|
|
3
|
-
* components can be tested at all.
|
|
4
|
-
*
|
|
5
|
-
* ── Why this package needed one ────────────────────────────────────────
|
|
6
|
-
*
|
|
7
|
-
* `vitest.config.ts` here included `src/**/*.spec.ts`, which cannot match a
|
|
8
|
-
* `.tsx` file — so every component in a published SDK (the composer, the
|
|
9
|
-
* message list, and now the block and canvas renderers a shopper taps) was
|
|
10
|
-
* structurally untestable. The same one-line config bug that left
|
|
11
|
-
* `apps/mobile-agent`'s screens uncovered, in a package customers ship.
|
|
12
|
-
*
|
|
13
|
-
* ── Why it is a second stand-in and not a shared one ───────────────────
|
|
14
|
-
*
|
|
15
|
-
* `apps/mobile-agent/src/test/react-native-mock.tsx` is the same idea for the
|
|
16
|
-
* agent app, and sharing them was tried and reverted. The only place a
|
|
17
|
-
* workspace-shared version could live is `@livechat/testing`, which depends
|
|
18
|
-
* on `@livechat/db`, `postgres` and `testcontainers` — so every React Native
|
|
19
|
-
* package would take a Postgres driver into its dev graph to borrow a `<div>`
|
|
20
|
-
* wrapper, and a bare `@livechat/testing/react-native-dom` specifier does not
|
|
21
|
-
* resolve from an app that does not depend on it. The two files also cover
|
|
22
|
-
* genuinely different sets: that one has `Alert`, `Modal`, `Switch`,
|
|
23
|
-
* `Animated` and a navigation stub; this one has the twelve primitives this
|
|
24
|
-
* package imports and nothing else.
|
|
25
|
-
*
|
|
26
|
-
* The real risk of a local copy is not drift — neither file is a contract —
|
|
27
|
-
* it is a stand-in that silently lacks a primitive a component starts using,
|
|
28
|
-
* which makes that component untestable again. `react-native-dom.spec.tsx`
|
|
29
|
-
* reads every `from 'react-native'` import in this package's source and
|
|
30
|
-
* fails if any name is missing from here, which is the half that matters.
|
|
31
|
-
*
|
|
32
|
-
* ── What it does NOT prove ─────────────────────────────────────────────
|
|
33
|
-
*
|
|
34
|
-
* Nothing native: not layout, not gestures, not keyboard behaviour, not that
|
|
35
|
-
* a `Pressable` has a large enough hit area, not that the package builds for
|
|
36
|
-
* a device. What it covers is the React layer, which is where component bugs
|
|
37
|
-
* are.
|
|
38
|
-
*
|
|
39
|
-
* `accessibilityLabel` becomes `aria-label` and `testID` becomes
|
|
40
|
-
* `data-testid`, deliberately: a test then asks for an element the way a
|
|
41
|
-
* screen reader would, so a control that is unlabelled in the app is also
|
|
42
|
-
* unfindable in the test.
|
|
43
|
-
*/
|
|
44
|
-
import React from 'react';
|
|
45
|
-
type AnyProps = Record<string, unknown> & {
|
|
46
|
-
children?: React.ReactNode;
|
|
47
|
-
};
|
|
48
|
-
export declare const View: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
49
|
-
export declare const SafeAreaView: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
50
|
-
export declare const ScrollView: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
51
|
-
export declare const Text: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
52
|
-
export declare const ActivityIndicator: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
53
|
-
export declare const Image: (props: AnyProps) => React.ReactElement<{
|
|
54
|
-
src: string;
|
|
55
|
-
alt: string;
|
|
56
|
-
}, string | React.JSXElementConstructor<any>>;
|
|
57
|
-
export declare const Pressable: (props: AnyProps) => React.DetailedReactHTMLElement<{
|
|
58
|
-
type: string;
|
|
59
|
-
onClick: (() => void) | undefined;
|
|
60
|
-
}, HTMLElement>;
|
|
61
|
-
export declare const TouchableOpacity: (props: AnyProps) => React.DetailedReactHTMLElement<{
|
|
62
|
-
type: string;
|
|
63
|
-
onClick: (() => void) | undefined;
|
|
64
|
-
}, HTMLElement>;
|
|
65
|
-
export declare const TextInput: (props: AnyProps) => React.DetailedReactHTMLElement<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>;
|
|
66
|
-
/**
|
|
67
|
-
* `FlatList` rendered eagerly, every row.
|
|
68
|
-
*
|
|
69
|
-
* A virtualising list would render a window, and a test asserting "the row is
|
|
70
|
-
* there" would then depend on a measured viewport that jsdom does not have.
|
|
71
|
-
* Every row is the honest simplification: it can over-report (a row present
|
|
72
|
-
* here might be off-screen on a device) and it cannot under-report, which is
|
|
73
|
-
* the direction a test should fail in.
|
|
74
|
-
*/
|
|
75
|
-
export declare const FlatList: (props: AnyProps) => React.DetailedReactHTMLElement<Record<string, unknown>, HTMLElement>;
|
|
76
|
-
export type ListRenderItemInfo<T> = {
|
|
77
|
-
item: T;
|
|
78
|
-
index: number;
|
|
79
|
-
};
|
|
80
|
-
export declare const Platform: {
|
|
81
|
-
OS: "ios";
|
|
82
|
-
select: (o: Record<string, unknown>) => unknown;
|
|
83
|
-
};
|
|
84
|
-
export declare const StyleSheet: {
|
|
85
|
-
create: <T>(styles: T) => T;
|
|
86
|
-
hairlineWidth: number;
|
|
87
|
-
flatten: (s: unknown) => unknown;
|
|
88
|
-
absoluteFill: {};
|
|
89
|
-
};
|
|
90
|
-
/** Opening a url. Recorded rather than performed, so a test can assert it. */
|
|
91
|
-
export declare const openedUrls: string[];
|
|
92
|
-
export declare const Linking: {
|
|
93
|
-
openURL: (url: string) => Promise<void>;
|
|
94
|
-
canOpenURL: () => Promise<boolean>;
|
|
95
|
-
};
|
|
96
|
-
export {};
|
|
97
|
-
//# sourceMappingURL=react-native-dom.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-native-dom.d.ts","sourceRoot":"","sources":["../../src/test/react-native-dom.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,CAAC;AAmCzE,eAAO,MAAM,IAAI,UALa,QAAQ,yEAKP,CAAC;AAChC,eAAO,MAAM,YAAY,UANK,QAAQ,yEAMC,CAAC;AACxC,eAAO,MAAM,UAAU,UAPO,QAAQ,yEAOD,CAAC;AACtC,eAAO,MAAM,IAAI,UARa,QAAQ,yEAQN,CAAC;AAEjC,eAAO,MAAM,iBAAiB,UAAqC,QAAQ,yEAE1E,CAAC;AAEF,eAAO,MAAM,KAAK,UAAyB,QAAQ;;;6CASlD,CAAC;AAqBF,eAAO,MAAM,SAAS,UAZsB,QAAQ;;oBAOa,IAAI;eAKnC,CAAC;AACnC,eAAO,MAAM,gBAAgB,UAbe,QAAQ;;oBAOa,IAAI;eAM5B,CAAC;AAE1C,eAAO,MAAM,SAAS,UAA6B,QAAQ,kGAY1D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,UAA4B,QAAQ,yEAqBxD,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,eAAO,MAAM,QAAQ;;gBAAqC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAAe,CAAC;AAEjG,eAAO,MAAM,UAAU;aACZ,CAAC,UAAW,CAAC,KAAG,CAAC;;iBAEb,OAAO;;CAErB,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,UAAU,EAAE,MAAM,EAAO,CAAC;AACvC,eAAO,MAAM,OAAO;mBACG,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;sBAGrB,OAAO,CAAC,OAAO,CAAC;CACvC,CAAC"}
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A DOM stand-in for the React Native primitives THIS package uses, so its
|
|
3
|
-
* components can be tested at all.
|
|
4
|
-
*
|
|
5
|
-
* ── Why this package needed one ────────────────────────────────────────
|
|
6
|
-
*
|
|
7
|
-
* `vitest.config.ts` here included `src/**/*.spec.ts`, which cannot match a
|
|
8
|
-
* `.tsx` file — so every component in a published SDK (the composer, the
|
|
9
|
-
* message list, and now the block and canvas renderers a shopper taps) was
|
|
10
|
-
* structurally untestable. The same one-line config bug that left
|
|
11
|
-
* `apps/mobile-agent`'s screens uncovered, in a package customers ship.
|
|
12
|
-
*
|
|
13
|
-
* ── Why it is a second stand-in and not a shared one ───────────────────
|
|
14
|
-
*
|
|
15
|
-
* `apps/mobile-agent/src/test/react-native-mock.tsx` is the same idea for the
|
|
16
|
-
* agent app, and sharing them was tried and reverted. The only place a
|
|
17
|
-
* workspace-shared version could live is `@livechat/testing`, which depends
|
|
18
|
-
* on `@livechat/db`, `postgres` and `testcontainers` — so every React Native
|
|
19
|
-
* package would take a Postgres driver into its dev graph to borrow a `<div>`
|
|
20
|
-
* wrapper, and a bare `@livechat/testing/react-native-dom` specifier does not
|
|
21
|
-
* resolve from an app that does not depend on it. The two files also cover
|
|
22
|
-
* genuinely different sets: that one has `Alert`, `Modal`, `Switch`,
|
|
23
|
-
* `Animated` and a navigation stub; this one has the twelve primitives this
|
|
24
|
-
* package imports and nothing else.
|
|
25
|
-
*
|
|
26
|
-
* The real risk of a local copy is not drift — neither file is a contract —
|
|
27
|
-
* it is a stand-in that silently lacks a primitive a component starts using,
|
|
28
|
-
* which makes that component untestable again. `react-native-dom.spec.tsx`
|
|
29
|
-
* reads every `from 'react-native'` import in this package's source and
|
|
30
|
-
* fails if any name is missing from here, which is the half that matters.
|
|
31
|
-
*
|
|
32
|
-
* ── What it does NOT prove ─────────────────────────────────────────────
|
|
33
|
-
*
|
|
34
|
-
* Nothing native: not layout, not gestures, not keyboard behaviour, not that
|
|
35
|
-
* a `Pressable` has a large enough hit area, not that the package builds for
|
|
36
|
-
* a device. What it covers is the React layer, which is where component bugs
|
|
37
|
-
* are.
|
|
38
|
-
*
|
|
39
|
-
* `accessibilityLabel` becomes `aria-label` and `testID` becomes
|
|
40
|
-
* `data-testid`, deliberately: a test then asks for an element the way a
|
|
41
|
-
* screen reader would, so a control that is unlabelled in the app is also
|
|
42
|
-
* unfindable in the test.
|
|
43
|
-
*/
|
|
44
|
-
import React from 'react';
|
|
45
|
-
/**
|
|
46
|
-
* RN props that mean something to a DOM element, translated. Everything else
|
|
47
|
-
* — `style`, `keyboardType` and the rest — is dropped rather than passed
|
|
48
|
-
* through, because React would warn about each one and the warnings would
|
|
49
|
-
* bury a real failure.
|
|
50
|
-
*/
|
|
51
|
-
function domProps(props) {
|
|
52
|
-
const out = {};
|
|
53
|
-
if (typeof props['accessibilityLabel'] === 'string') {
|
|
54
|
-
out['aria-label'] = props['accessibilityLabel'];
|
|
55
|
-
}
|
|
56
|
-
if (typeof props['accessibilityHint'] === 'string') {
|
|
57
|
-
out['aria-description'] = props['accessibilityHint'];
|
|
58
|
-
}
|
|
59
|
-
if (typeof props['testID'] === 'string') {
|
|
60
|
-
out['data-testid'] = props['testID'];
|
|
61
|
-
}
|
|
62
|
-
if (props['accessibilityRole'] === 'button' || props['accessibilityRole'] === 'link') {
|
|
63
|
-
out['role'] = props['accessibilityRole'];
|
|
64
|
-
}
|
|
65
|
-
if (props['disabled'] === true) {
|
|
66
|
-
out['disabled'] = true;
|
|
67
|
-
out['aria-disabled'] = true;
|
|
68
|
-
}
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
function view(tag) {
|
|
72
|
-
return function Mock(props) {
|
|
73
|
-
return React.createElement(tag, domProps(props), props['children']);
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
export const View = view('div');
|
|
77
|
-
export const SafeAreaView = view('div');
|
|
78
|
-
export const ScrollView = view('div');
|
|
79
|
-
export const Text = view('span');
|
|
80
|
-
export const ActivityIndicator = function ActivityIndicator(props) {
|
|
81
|
-
return React.createElement('progress', domProps(props));
|
|
82
|
-
};
|
|
83
|
-
export const Image = function Image(props) {
|
|
84
|
-
const source = props['source'];
|
|
85
|
-
return React.createElement('img', {
|
|
86
|
-
...domProps(props),
|
|
87
|
-
src: source?.uri ?? '',
|
|
88
|
-
// RN has no alt; `accessibilityLabel` is the equivalent and an image with
|
|
89
|
-
// neither should be findable as decorative rather than invisible.
|
|
90
|
-
alt: typeof props['accessibilityLabel'] === 'string' ? props['accessibilityLabel'] : '',
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* `Pressable` as a real `<button>`.
|
|
95
|
-
*
|
|
96
|
-
* A `<div onClick>` would be clickable in a test and unreachable by keyboard,
|
|
97
|
-
* which is exactly the distinction a test should keep: `getByRole('button')`
|
|
98
|
-
* finds this, and `userEvent.click` fires `onPress`.
|
|
99
|
-
*/
|
|
100
|
-
const pressable = function Pressable(props) {
|
|
101
|
-
const onPress = props['onPress'];
|
|
102
|
-
return React.createElement('button', {
|
|
103
|
-
...domProps(props),
|
|
104
|
-
type: 'button',
|
|
105
|
-
onClick: typeof onPress === 'function' ? onPress : undefined,
|
|
106
|
-
}, props['children']);
|
|
107
|
-
};
|
|
108
|
-
export const Pressable = pressable;
|
|
109
|
-
export const TouchableOpacity = pressable;
|
|
110
|
-
export const TextInput = function TextInput(props) {
|
|
111
|
-
const onChangeText = props['onChangeText'];
|
|
112
|
-
return React.createElement('input', {
|
|
113
|
-
...domProps(props),
|
|
114
|
-
value: props['value'] ?? '',
|
|
115
|
-
placeholder: props['placeholder'],
|
|
116
|
-
onChange: (e) => {
|
|
117
|
-
if (typeof onChangeText === 'function') {
|
|
118
|
-
onChangeText(e.target.value);
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
};
|
|
123
|
-
/**
|
|
124
|
-
* `FlatList` rendered eagerly, every row.
|
|
125
|
-
*
|
|
126
|
-
* A virtualising list would render a window, and a test asserting "the row is
|
|
127
|
-
* there" would then depend on a measured viewport that jsdom does not have.
|
|
128
|
-
* Every row is the honest simplification: it can over-report (a row present
|
|
129
|
-
* here might be off-screen on a device) and it cannot under-report, which is
|
|
130
|
-
* the direction a test should fail in.
|
|
131
|
-
*/
|
|
132
|
-
export const FlatList = function FlatList(props) {
|
|
133
|
-
const data = props['data'] ?? [];
|
|
134
|
-
const renderItem = props['renderItem'];
|
|
135
|
-
const keyExtractor = props['keyExtractor'];
|
|
136
|
-
return React.createElement('div', domProps(props), props['ListHeaderComponent'], data.length === 0
|
|
137
|
-
? props['ListEmptyComponent']
|
|
138
|
-
: data.map((item, index) => React.createElement('div', { key: keyExtractor?.(item, index) ?? String(index) }, renderItem?.({ item, index }))), props['ListFooterComponent']);
|
|
139
|
-
};
|
|
140
|
-
export const Platform = { OS: 'ios', select: (o) => o['ios'] };
|
|
141
|
-
export const StyleSheet = {
|
|
142
|
-
create: (styles) => styles,
|
|
143
|
-
hairlineWidth: 1,
|
|
144
|
-
flatten: (s) => s,
|
|
145
|
-
absoluteFill: {},
|
|
146
|
-
};
|
|
147
|
-
/** Opening a url. Recorded rather than performed, so a test can assert it. */
|
|
148
|
-
export const openedUrls = [];
|
|
149
|
-
export const Linking = {
|
|
150
|
-
openURL: async (url) => {
|
|
151
|
-
openedUrls.push(url);
|
|
152
|
-
},
|
|
153
|
-
canOpenURL: async () => true,
|
|
154
|
-
};
|
|
155
|
-
//# sourceMappingURL=react-native-dom.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-native-dom.js","sourceRoot":"","sources":["../../src/test/react-native-dom.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-native-dom.spec.d.ts","sourceRoot":"","sources":["../../src/test/react-native-dom.spec.ts"],"names":[],"mappings":""}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The DOM stand-in covers every primitive this package imports.
|
|
3
|
-
*
|
|
4
|
-
* ── The failure this catches ───────────────────────────────────────────
|
|
5
|
-
*
|
|
6
|
-
* A stand-in is not a contract, so it cannot "drift" in the usual sense.
|
|
7
|
-
* What it can do is silently lack a primitive a component starts using:
|
|
8
|
-
* `import { Modal } from 'react-native'` resolves to `undefined`, the
|
|
9
|
-
* component throws at render, and the symptom is a test file that fails to
|
|
10
|
-
* collect — which reads as a broken test rather than as an incomplete
|
|
11
|
-
* harness. Worse, somebody deletes the test.
|
|
12
|
-
*
|
|
13
|
-
* So this reads the source rather than trusting it: every name imported from
|
|
14
|
-
* `react-native` anywhere in `src/` must be exported by the stand-in.
|
|
15
|
-
*/
|
|
16
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
|
17
|
-
import { dirname, join } from 'node:path';
|
|
18
|
-
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { describe, expect, it } from 'vitest';
|
|
20
|
-
import * as standIn from './react-native-dom';
|
|
21
|
-
/*
|
|
22
|
-
* `fileURLToPath(dirname(...))`, not `new URL('..').pathname`.
|
|
23
|
-
*
|
|
24
|
-
* The first attempt used the latter and resolved to `/src` — vite rewrites
|
|
25
|
-
* `import.meta.url` for a transformed module, so the URL is root-relative and
|
|
26
|
-
* `.pathname` drops the part that matters. The failure was an ENOENT on
|
|
27
|
-
* `/src/index.ts`, which at least was loud; a version that had silently
|
|
28
|
-
* scanned an empty directory would have passed.
|
|
29
|
-
*/
|
|
30
|
-
const SRC = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
31
|
-
function walk(dir, out = []) {
|
|
32
|
-
for (const entry of readdirSync(dir)) {
|
|
33
|
-
const full = join(dir, entry);
|
|
34
|
-
if (statSync(full).isDirectory()) {
|
|
35
|
-
walk(full, out);
|
|
36
|
-
}
|
|
37
|
-
else if (/\.tsx?$/.test(entry) && !entry.includes('.spec.')) {
|
|
38
|
-
out.push(full);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return out;
|
|
42
|
-
}
|
|
43
|
-
/** Every name imported from `react-native`, across the package. */
|
|
44
|
-
function importedNames() {
|
|
45
|
-
const names = new Set();
|
|
46
|
-
for (const file of walk(SRC)) {
|
|
47
|
-
const src = readFileSync(file, 'utf8');
|
|
48
|
-
for (const m of src.matchAll(/import \{([^}]*)\} from 'react-native';/gs)) {
|
|
49
|
-
for (const raw of (m[1] ?? '').split(',')) {
|
|
50
|
-
const name = raw.trim().replace(/^type\s+/, '');
|
|
51
|
-
if (name.length > 0) {
|
|
52
|
-
names.add(name);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return [...names].sort();
|
|
58
|
-
}
|
|
59
|
-
describe('react-native stand-in', () => {
|
|
60
|
-
const imported = importedNames();
|
|
61
|
-
it('is reading the source — the scan found the imports', () => {
|
|
62
|
-
// A floor, because an empty list would make the assertion below trivially
|
|
63
|
-
// true forever. Twelve at the time of writing.
|
|
64
|
-
expect(imported.length).toBeGreaterThanOrEqual(10);
|
|
65
|
-
expect(imported).toContain('View');
|
|
66
|
-
expect(imported).toContain('Text');
|
|
67
|
-
});
|
|
68
|
-
it('exports every primitive the package imports', () => {
|
|
69
|
-
// `ListRenderItemInfo` is a TYPE and is erased, so it is not a runtime
|
|
70
|
-
// export of the stand-in — but it must still be declared there or the
|
|
71
|
-
// alias breaks the typecheck, which `tsc --noEmit` covers.
|
|
72
|
-
const runtime = imported.filter((n) => n !== 'ListRenderItemInfo');
|
|
73
|
-
const missing = runtime.filter((n) => !(n in standIn));
|
|
74
|
-
expect(missing).toEqual([]);
|
|
75
|
-
});
|
|
76
|
-
it('exports components that actually render', () => {
|
|
77
|
-
// `in` would be satisfied by an exported `undefined`.
|
|
78
|
-
for (const name of ['View', 'Text', 'Pressable', 'TextInput', 'FlatList', 'Image']) {
|
|
79
|
-
expect(typeof standIn[name]).toBe('function');
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
//# sourceMappingURL=react-native-dom.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"react-native-dom.spec.js","sourceRoot":"","sources":["../../src/test/react-native-dom.spec.ts"],"names":[],"mappings":"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"}
|