@nyaruka/temba-components 0.156.17 → 0.157.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.
- package/CHANGELOG.md +17 -0
- package/dist/temba-components.js +1189 -767
- package/dist/temba-components.js.map +1 -1
- package/package.json +1 -1
- package/src/display/Chat.ts +14 -0
- package/src/display/Label.ts +156 -2
- package/src/display/Options.ts +71 -16
- package/src/display/TembaUser.ts +23 -5
- package/src/events/eventRenderers.ts +104 -41
- package/src/excellent/caret-utils.ts +0 -1
- package/src/flow/RevisionsWindow.ts +53 -9
- package/src/flow/nodes/shared.ts +14 -0
- package/src/flow/nodes/split_by_llm_categorize.ts +33 -8
- package/src/flow/revision-summary.ts +25 -0
- package/src/flow/utils.ts +38 -40
- package/src/form/ArrayEditor.ts +9 -11
- package/src/form/Checkbox.ts +2 -2
- package/src/form/Compose.ts +1 -1
- package/src/form/FieldElement.ts +8 -8
- package/src/form/KeyValueEditor.ts +4 -4
- package/src/form/MessageEditor.ts +2 -3
- package/src/form/RangePicker.ts +17 -17
- package/src/form/TembaSlider.ts +10 -10
- package/src/form/TemplateEditor.ts +4 -4
- package/src/form/TextInput.ts +19 -1
- package/src/form/select/Omnibox.ts +22 -19
- package/src/form/select/Select.ts +379 -171
- package/src/form/select/WorkspaceSelect.ts +7 -1
- package/src/layout/Accordion.ts +2 -2
- package/src/layout/Modax.ts +1 -1
- package/src/list/SortableList.ts +159 -0
- package/src/live/ContactChat.ts +46 -44
- package/src/live/ContactDetails.ts +1 -0
- package/src/live/ContactFieldEditor.ts +38 -31
- package/src/live/FieldManager.ts +4 -4
- package/src/styles/designTokens.ts +145 -0
- package/src/styles/pillVariants.ts +136 -0
- package/static/css/temba-components.css +106 -28
- package/web-test-runner.config.mjs +98 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* TextIt Design System pill color variants — single source of truth.
|
|
5
|
+
*
|
|
6
|
+
* Provides the color triple (background / foreground / border) keyed
|
|
7
|
+
* off `.pill-{type}` plus the JS-side taxonomy (`PILL_TYPES`,
|
|
8
|
+
* `PILL_TYPE_ICONS`, `iconToPillType`) consumed by Select.ts and
|
|
9
|
+
* flow/utils.ts.
|
|
10
|
+
*
|
|
11
|
+
* Variant theming:
|
|
12
|
+
* - .pill-contact / .pill-group: full ramp derived from --accent
|
|
13
|
+
* via `color-mix(in oklab, …)`. Override --accent to re-theme.
|
|
14
|
+
* - .pill-flow, .pill-channel: bg/border derived from --flow /
|
|
15
|
+
* --channel via color-mix; text + icon use the anchor directly.
|
|
16
|
+
* - .pill-field: bg/border are fixed at the Tailwind yellow ramp
|
|
17
|
+
* (yellow-100 / yellow-300) because yellow has too little
|
|
18
|
+
* contrast against white to color-mix into a recognizable swatch.
|
|
19
|
+
* Text uses yellow-900 for readability; the icon uses --field
|
|
20
|
+
* directly, which is the only knob a host page can re-theme.
|
|
21
|
+
* - .pill-neutral / .pill-label / .pill-keyword: greys; not
|
|
22
|
+
* anchor-driven.
|
|
23
|
+
*
|
|
24
|
+
* Shape (height, padding, radius, icon spacing) is the consumer's
|
|
25
|
+
* concern, since pill use-cases differ: Select chips have a remove
|
|
26
|
+
* button on the right, ContactDetails pills are clickable links, etc.
|
|
27
|
+
*
|
|
28
|
+
* To add a new variant: extend `PILL_TYPES`, optionally add an entry
|
|
29
|
+
* to `PILL_TYPE_ICONS` / `ICON_TO_PILL_TYPE`, append a `.pill-{type}`
|
|
30
|
+
* block below, and reference an anchor in `designTokens.ts`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Recognized pill variants. Anything outside this set falls back to
|
|
34
|
+
* `pill-neutral` (or is rejected by callers as not a pill at all). */
|
|
35
|
+
export const PILL_TYPES: ReadonlySet<string> = new Set([
|
|
36
|
+
'neutral',
|
|
37
|
+
'flow',
|
|
38
|
+
'group',
|
|
39
|
+
'contact',
|
|
40
|
+
'field',
|
|
41
|
+
'label',
|
|
42
|
+
'keyword',
|
|
43
|
+
'channel'
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
/** Default icon name for each pill variant. Used when a consumer
|
|
47
|
+
* specifies `type` but not `icon` — keeps Omnibox-style options and
|
|
48
|
+
* Django-form-rendered options visually consistent without making the
|
|
49
|
+
* data layer set both fields. */
|
|
50
|
+
export const PILL_TYPE_ICONS: Readonly<Record<string, string>> = {
|
|
51
|
+
group: 'group',
|
|
52
|
+
contact: 'contact',
|
|
53
|
+
field: 'fields',
|
|
54
|
+
flow: 'flow',
|
|
55
|
+
label: 'label'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Inverse mapping: icon name (alias or resolved SVG id) → pill type.
|
|
59
|
+
* Both forms are valid since flow-action items pass through either. */
|
|
60
|
+
const ICON_TO_PILL_TYPE: Readonly<Record<string, string>> = {
|
|
61
|
+
flow: 'flow',
|
|
62
|
+
group: 'group',
|
|
63
|
+
contact: 'contact',
|
|
64
|
+
contacts: 'contact',
|
|
65
|
+
field: 'field',
|
|
66
|
+
fields: 'field',
|
|
67
|
+
label: 'label',
|
|
68
|
+
// resolved Icon enum SVG ids
|
|
69
|
+
'users-01': 'group',
|
|
70
|
+
'atom-01': 'group',
|
|
71
|
+
'user-01': 'contact',
|
|
72
|
+
'tag-01': 'label'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const iconToPillType = (icon?: string): string | undefined => {
|
|
76
|
+
if (!icon) return undefined;
|
|
77
|
+
if (ICON_TO_PILL_TYPE[icon]) return ICON_TO_PILL_TYPE[icon];
|
|
78
|
+
// Legacy alias prefix (e.g. 'group_smart' → 'group').
|
|
79
|
+
if (icon.startsWith('group')) return 'group';
|
|
80
|
+
return undefined;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const pillVariants = css`
|
|
84
|
+
.pill-neutral {
|
|
85
|
+
background: var(--sunken);
|
|
86
|
+
color: var(--text-1);
|
|
87
|
+
border-color: var(--border);
|
|
88
|
+
--icon-color: var(--text-2);
|
|
89
|
+
}
|
|
90
|
+
.pill-flow {
|
|
91
|
+
background: color-mix(in oklab, var(--flow) 12%, white);
|
|
92
|
+
color: var(--flow);
|
|
93
|
+
border-color: color-mix(in oklab, var(--flow) 25%, white);
|
|
94
|
+
--icon-color: var(--flow);
|
|
95
|
+
}
|
|
96
|
+
/* Recipient color — shared by contacts and groups. */
|
|
97
|
+
.pill-contact,
|
|
98
|
+
.pill-group {
|
|
99
|
+
background: var(--accent-100);
|
|
100
|
+
color: var(--accent-700);
|
|
101
|
+
border-color: var(--accent-200);
|
|
102
|
+
--icon-color: var(--accent-700);
|
|
103
|
+
}
|
|
104
|
+
.pill-channel {
|
|
105
|
+
background: color-mix(in oklab, var(--channel) 12%, white);
|
|
106
|
+
color: var(--channel);
|
|
107
|
+
border-color: color-mix(in oklab, var(--channel) 25%, white);
|
|
108
|
+
--icon-color: var(--channel);
|
|
109
|
+
}
|
|
110
|
+
.pill-field {
|
|
111
|
+
/* Yellow has very low contrast against white, so the color-mix
|
|
112
|
+
approach used by other variants washes out at any readable mix
|
|
113
|
+
percentage. We use the Tailwind yellow ramp directly for bg
|
|
114
|
+
(yellow-100) / border (yellow-300) / text (yellow-900). The
|
|
115
|
+
icon hue stays anchored to --field so a host page can still
|
|
116
|
+
re-theme the variant by overriding that one token. */
|
|
117
|
+
background: #fef9c3;
|
|
118
|
+
color: #854d0e;
|
|
119
|
+
border-color: #fde68a;
|
|
120
|
+
--icon-color: var(--field);
|
|
121
|
+
}
|
|
122
|
+
.pill-keyword {
|
|
123
|
+
background: var(--sunken);
|
|
124
|
+
color: var(--text-1);
|
|
125
|
+
border-color: var(--border);
|
|
126
|
+
--icon-color: var(--text-2);
|
|
127
|
+
font-family: var(--font-mono);
|
|
128
|
+
font-size: 11.5px;
|
|
129
|
+
}
|
|
130
|
+
.pill-label {
|
|
131
|
+
background: var(--sunken);
|
|
132
|
+
color: var(--text-2);
|
|
133
|
+
border-color: var(--border);
|
|
134
|
+
--icon-color: var(--text-2);
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
@@ -6,7 +6,80 @@
|
|
|
6
6
|
|
|
7
7
|
html {
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
/* ─── TextIt Design System tokens ─────────────────────────────────────
|
|
10
|
+
Single source of truth. Cascades through Shadow DOM into every
|
|
11
|
+
component. Legacy tokens below are aliased to these — keep both in
|
|
12
|
+
sync if the design system evolves. */
|
|
13
|
+
|
|
14
|
+
/* accent ramp — derived from a single anchor via OKLab mixing */
|
|
15
|
+
--accent: #2A6FB5;
|
|
16
|
+
--accent-50: color-mix(in oklab, var(--accent) 6%, white);
|
|
17
|
+
--accent-100: color-mix(in oklab, var(--accent) 12%, white);
|
|
18
|
+
--accent-200: color-mix(in oklab, var(--accent) 25%, white);
|
|
19
|
+
--accent-300: color-mix(in oklab, var(--accent) 45%, white);
|
|
20
|
+
--accent-400: color-mix(in oklab, var(--accent) 75%, white);
|
|
21
|
+
--accent-500: var(--accent);
|
|
22
|
+
--accent-600: color-mix(in oklab, var(--accent) 88%, black);
|
|
23
|
+
--accent-700: color-mix(in oklab, var(--accent) 75%, black);
|
|
24
|
+
--accent-800: color-mix(in oklab, var(--accent) 60%, black);
|
|
25
|
+
--accent-900: color-mix(in oklab, var(--accent) 45%, black);
|
|
26
|
+
|
|
27
|
+
/* neutrals */
|
|
28
|
+
--bg: #F6F7F9;
|
|
29
|
+
--surface: #FFFFFF;
|
|
30
|
+
--sunken: #F1F3F5;
|
|
31
|
+
--border: #E6E8EC;
|
|
32
|
+
--border-strong: #D2D6DC;
|
|
33
|
+
--text-1: #1A1F26;
|
|
34
|
+
--text-2: #4D5664;
|
|
35
|
+
--text-3: #7B8593;
|
|
36
|
+
--text-4: #A2ABB8;
|
|
37
|
+
|
|
38
|
+
/* status — full set */
|
|
39
|
+
--success: #16A34A;
|
|
40
|
+
--success-bg: #E8F6EE;
|
|
41
|
+
--success-border: #BFE5CD;
|
|
42
|
+
--info: #2563EB;
|
|
43
|
+
--info-bg: #E8F0FE;
|
|
44
|
+
--info-border: #C7D7F8;
|
|
45
|
+
--warning: #B45309;
|
|
46
|
+
--warning-bg: #FDF3E2;
|
|
47
|
+
--warning-border: #F2D9A9;
|
|
48
|
+
--danger: #D03F3F;
|
|
49
|
+
--danger-bg: #FCEBEB;
|
|
50
|
+
--danger-border: #F4C8C8;
|
|
51
|
+
--neutral: #6B7280;
|
|
52
|
+
--neutral-bg: #EEF0F3;
|
|
53
|
+
--neutral-border: #D8DCE2;
|
|
54
|
+
|
|
55
|
+
/* type */
|
|
56
|
+
--font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
57
|
+
--font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
58
|
+
--w-regular: 400;
|
|
59
|
+
--w-medium: 500;
|
|
60
|
+
--w-semibold: 600;
|
|
61
|
+
--w-bold: 700;
|
|
62
|
+
|
|
63
|
+
/* shape */
|
|
64
|
+
--r: 8px;
|
|
65
|
+
--r-xs: 2px;
|
|
66
|
+
--r-sm: 4px;
|
|
67
|
+
--r-lg: 12px;
|
|
68
|
+
|
|
69
|
+
/* density */
|
|
70
|
+
--row-h: 36px;
|
|
71
|
+
--input-h: 34px;
|
|
72
|
+
--pad: 10px;
|
|
73
|
+
--gap: 14px;
|
|
74
|
+
|
|
75
|
+
/* shadows */
|
|
76
|
+
--shadow-1: 0 1px 1px rgba(15, 22, 36, 0.04), 0 1px 2px rgba(15, 22, 36, 0.04);
|
|
77
|
+
--shadow-2: 0 1px 1px rgba(15, 22, 36, 0.04), 0 4px 12px rgba(15, 22, 36, 0.06);
|
|
78
|
+
--shadow-3: 0 6px 20px rgba(15, 22, 36, 0.10), 0 2px 6px rgba(15, 22, 36, 0.06);
|
|
79
|
+
|
|
80
|
+
/* ─── legacy aliases — point at the DS tokens above ────────────────── */
|
|
81
|
+
|
|
82
|
+
--font-family: var(--font);
|
|
10
83
|
--primary-rgb: 35, 135, 202;
|
|
11
84
|
--secondary-rgb: 140, 51, 140;
|
|
12
85
|
--tertiary-rgb: 135, 202, 35;
|
|
@@ -21,37 +94,41 @@
|
|
|
21
94
|
--select-input-height: inherit;
|
|
22
95
|
|
|
23
96
|
--disabled-opacity: 0.6;
|
|
24
|
-
--curvature:
|
|
25
|
-
--curvature-widget:
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
--color-
|
|
97
|
+
--curvature: var(--r-sm);
|
|
98
|
+
--curvature-widget: var(--r-sm);
|
|
99
|
+
--focus: #5b9ce5;
|
|
100
|
+
--focus-muted: color-mix(in oklab, var(--focus) 60%, white);
|
|
101
|
+
--focus-halo: 0 0 0 3px
|
|
102
|
+
color-mix(in oklab, var(--focus) 30%, transparent);
|
|
103
|
+
--color-focus: var(--focus-muted);
|
|
104
|
+
--color-widget-bg: var(--surface);
|
|
105
|
+
--color-widget-bg-focused: var(--surface);
|
|
106
|
+
--color-widget-border: var(--border-strong);
|
|
107
|
+
|
|
108
|
+
--color-options-bg: var(--surface);
|
|
32
109
|
|
|
33
110
|
/* primary colors, should be dark */
|
|
34
|
-
--color-selection:
|
|
35
|
-
--color-success:
|
|
111
|
+
--color-selection: var(--accent-50);
|
|
112
|
+
--color-success: var(--success);
|
|
36
113
|
|
|
37
|
-
--widget-box-shadow:
|
|
38
|
-
--widget-box-shadow-focused:
|
|
114
|
+
--widget-box-shadow: none;
|
|
115
|
+
--widget-box-shadow-focused: var(--focus-halo);
|
|
39
116
|
--widget-box-shadow-focused-error: 0 0 0 3px rgba(var(--error-rgb), 0.3);
|
|
40
117
|
|
|
41
|
-
--shadow:
|
|
42
|
-
--shadow-widget:
|
|
118
|
+
--shadow: var(--shadow-1);
|
|
119
|
+
--shadow-widget: var(--shadow-1);
|
|
43
120
|
|
|
44
121
|
/* page text, borders, widgets */
|
|
45
|
-
--color-text:
|
|
46
|
-
--color-widget-text:
|
|
47
|
-
--color-borders:
|
|
48
|
-
--color-placeholder:
|
|
122
|
+
--color-text: var(--text-1);
|
|
123
|
+
--color-widget-text: var(--text-1);
|
|
124
|
+
--color-borders: var(--border);
|
|
125
|
+
--color-placeholder: var(--text-3);
|
|
49
126
|
|
|
50
127
|
/* light colors, panel backgrounds, selection, etc */
|
|
51
|
-
--color-primary-light:
|
|
128
|
+
--color-primary-light: var(--sunken);
|
|
52
129
|
--color-secondary-light: #ccc;
|
|
53
130
|
|
|
54
|
-
--color-label:
|
|
131
|
+
--color-label: var(--text-1);
|
|
55
132
|
|
|
56
133
|
/* dark colors, nav bar, buttons, etc */
|
|
57
134
|
--color-primary-dark: rgb(var(--primary-rgb));
|
|
@@ -61,12 +138,11 @@
|
|
|
61
138
|
--color-text-light: rgba(255, 255, 255, 1);
|
|
62
139
|
--color-text-dark: rgba(0, 0, 0, 0.8);
|
|
63
140
|
--color-text-dark-secondary: rgba(0, 0, 0, 0.25);
|
|
64
|
-
--color-text-help:
|
|
141
|
+
--color-text-help: var(--text-3);
|
|
65
142
|
--color-tertiary: rgb(var(--tertiary-rgb));
|
|
66
143
|
|
|
67
144
|
--help-text-size: 0.85em;
|
|
68
145
|
--help-text-margin-left: 0.3em;
|
|
69
|
-
--color-text-help: rgb(120, 120, 120);
|
|
70
146
|
|
|
71
147
|
/* solid overlays with text */
|
|
72
148
|
--color-overlay-dark: rgba(0, 0, 0, 0.2);
|
|
@@ -116,9 +192,10 @@
|
|
|
116
192
|
|
|
117
193
|
--transition-speed: 250ms;
|
|
118
194
|
--event-padding: 0.5em 1em;
|
|
119
|
-
--temba-select-selected-padding:
|
|
120
|
-
--temba-select-selected-line-height:
|
|
121
|
-
--temba-select-selected-font-size:
|
|
195
|
+
--temba-select-selected-padding: 0 var(--pad);
|
|
196
|
+
--temba-select-selected-line-height: 1.4;
|
|
197
|
+
--temba-select-selected-font-size: 13.5px;
|
|
198
|
+
--temba-select-min-height: var(--input-h);
|
|
122
199
|
|
|
123
200
|
--font-size: 14px;
|
|
124
201
|
--button-font-size: 1.125rem;
|
|
@@ -126,8 +203,9 @@
|
|
|
126
203
|
--header-bg: var(--color-primary-dark);
|
|
127
204
|
--header-text: var(--color-text-light);
|
|
128
205
|
|
|
129
|
-
--temba-textinput-padding:
|
|
130
|
-
--temba-textinput-font-size:
|
|
206
|
+
--temba-textinput-padding: 7px var(--pad);
|
|
207
|
+
--temba-textinput-font-size: 13.5px;
|
|
208
|
+
--temba-textinput-min-height: var(--input-h);
|
|
131
209
|
|
|
132
210
|
--options-block-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.03);
|
|
133
211
|
--options-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
|
@@ -2,6 +2,100 @@
|
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
3
|
process.env.PUPPETEER_DISABLE_HEADLESS_WARNING = '1';
|
|
4
4
|
import { puppeteerLauncher } from '@web/test-runner-puppeteer';
|
|
5
|
+
import { defaultReporter, summaryReporter } from '@web/test-runner';
|
|
6
|
+
|
|
7
|
+
// Workaround for a wtr OOM under coverage on this branch. With our
|
|
8
|
+
// volume of instrumented code, the merged istanbul-coverage payload
|
|
9
|
+
// across all 121 test sessions overflows V8's max string length
|
|
10
|
+
// (~512MB) when `getTestCoverage` deep-clones it via
|
|
11
|
+
// `JSON.parse(JSON.stringify(coverages))`. That clone exists only to
|
|
12
|
+
// insulate watch mode from istanbul's in-place mutation of the
|
|
13
|
+
// originals — in non-watch coverage mode the clone is dead weight.
|
|
14
|
+
// The throw is caught inside `onSessionFinished`'s try/catch, which
|
|
15
|
+
// calls `runner.stop(error)` and silently exits 1 (the `console.error`
|
|
16
|
+
// it logs is swallowed by wtr's BufferedLogger before the final
|
|
17
|
+
// reportEnd flush ever runs).
|
|
18
|
+
//
|
|
19
|
+
// We can't import `getTestCoverage` directly (it's a transitive dep
|
|
20
|
+
// not re-exported by `@web/test-runner`), so we shim JSON.parse: when
|
|
21
|
+
// it's handed the sentinel produced by our JSON.stringify shim, it
|
|
22
|
+
// returns the original `coverages` array instead of the stringified
|
|
23
|
+
// copy. The shim is intentionally narrow:
|
|
24
|
+
//
|
|
25
|
+
// - Gated on `WTR_COVERAGE_SHIM` (defaults on when --coverage is in
|
|
26
|
+
// argv; can be force-disabled by setting it to "0") so it never
|
|
27
|
+
// runs in plain `pnpm test`, only in the coverage path.
|
|
28
|
+
// - Sentinel is a runtime-random string, so a fixture/cached body
|
|
29
|
+
// containing the literal can't collide.
|
|
30
|
+
// - `__pendingCoverages` is cleared on every parse — matched OR not —
|
|
31
|
+
// so the ~512MB reference never lingers past one call pair, and
|
|
32
|
+
// mismatched call sequences never silently corrupt other parses.
|
|
33
|
+
// - The shape check `__looksLikeCoverageArray` only matches arrays
|
|
34
|
+
// whose first element is an object keyed by absolute file paths
|
|
35
|
+
// pointing at istanbul-shaped entries; it can't be triggered by
|
|
36
|
+
// ordinary JSON the orchestrator stringifies (HTTP responses, etc.)
|
|
37
|
+
//
|
|
38
|
+
// A wtr update that changes how getTestCoverage clones would make the
|
|
39
|
+
// shim a no-op (`__looksLikeCoverageArray` returns false) — we'd then
|
|
40
|
+
// regress to the original OOM rather than silently corrupt data, and
|
|
41
|
+
// the next coverage run would surface it immediately. Long-term, the
|
|
42
|
+
// right fix is patch-package on `@web/test-runner-core`'s
|
|
43
|
+
// `getTestCoverage` to skip the clone in non-watch mode; this shim is
|
|
44
|
+
// scoped to keep that scope-creep out of this PR.
|
|
45
|
+
const __WTR_COVERAGE_SHIM_ON =
|
|
46
|
+
process.env.WTR_COVERAGE_SHIM !== '0' &&
|
|
47
|
+
(process.env.WTR_COVERAGE_SHIM === '1' ||
|
|
48
|
+
process.argv.some((a) => a === '--coverage' || a === '--watch-coverage'));
|
|
49
|
+
|
|
50
|
+
if (__WTR_COVERAGE_SHIM_ON) {
|
|
51
|
+
const __origStringify = JSON.stringify;
|
|
52
|
+
const __origParse = JSON.parse;
|
|
53
|
+
// Random sentinel so the literal string in user content can't
|
|
54
|
+
// accidentally trigger the parse shim.
|
|
55
|
+
const __CLONE_SENTINEL =
|
|
56
|
+
'__WTR_COVERAGE_CLONE__' + Math.random().toString(36).slice(2);
|
|
57
|
+
let __pendingCoverages = null;
|
|
58
|
+
|
|
59
|
+
const __looksLikeCoverageArray = (value) => {
|
|
60
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
61
|
+
const first = value[0];
|
|
62
|
+
if (!first || typeof first !== 'object' || Array.isArray(first)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const keys = Object.keys(first);
|
|
66
|
+
if (keys.length === 0) return false;
|
|
67
|
+
if (!keys.every((k) => k.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(k))) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const entry = first[keys[0]];
|
|
71
|
+
return (
|
|
72
|
+
entry &&
|
|
73
|
+
typeof entry === 'object' &&
|
|
74
|
+
(entry.statementMap || entry.fnMap || entry.b || entry.s)
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
JSON.stringify = function (value, ...rest) {
|
|
79
|
+
if (__looksLikeCoverageArray(value)) {
|
|
80
|
+
// A second matched stringify before a matched parse would
|
|
81
|
+
// otherwise silently drop the first payload — clear first.
|
|
82
|
+
__pendingCoverages = value;
|
|
83
|
+
return __CLONE_SENTINEL;
|
|
84
|
+
}
|
|
85
|
+
return __origStringify(value, ...rest);
|
|
86
|
+
};
|
|
87
|
+
JSON.parse = function (text, ...rest) {
|
|
88
|
+
// Always clear pending on parse (matched or not). Keeps the ~512MB
|
|
89
|
+
// reference from outliving a single call pair even if the matched
|
|
90
|
+
// parse never arrives (e.g. wtr throws between the two calls).
|
|
91
|
+
const pending = __pendingCoverages;
|
|
92
|
+
__pendingCoverages = null;
|
|
93
|
+
if (text === __CLONE_SENTINEL && pending) {
|
|
94
|
+
return pending;
|
|
95
|
+
}
|
|
96
|
+
return __origParse(text, ...rest);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
5
99
|
import { esbuildPlugin } from '@web/dev-server-esbuild';
|
|
6
100
|
import fs from 'fs';
|
|
7
101
|
import * as path from 'path';
|
|
@@ -346,6 +440,10 @@ export default {
|
|
|
346
440
|
coverageConfig: {
|
|
347
441
|
include: ['src/**']
|
|
348
442
|
},
|
|
443
|
+
reporters: [
|
|
444
|
+
defaultReporter({ reportTestResults: true, reportTestProgress: true }),
|
|
445
|
+
summaryReporter({ flatten: true })
|
|
446
|
+
],
|
|
349
447
|
testFramework: {
|
|
350
448
|
config: {
|
|
351
449
|
timeout: '10000'
|