@sumaris-net/ngx-components 21.0.0-rc2 → 21.0.0-rc21

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.
@@ -2,7 +2,7 @@
2
2
  "name": "ngx-sumaris-components",
3
3
  "short_name": "ngx-sumaris-components",
4
4
  "manifest_version": 1,
5
- "version": "21.0.0-rc2",
5
+ "version": "21.0.0-rc21",
6
6
  "default_locale": "fr",
7
7
  "description": "Angular components for building beautiful and responsive Apps",
8
8
  "icons": [{
@@ -1,5 +1,5 @@
1
- //@use './ionic/ionic.functions.string';
2
- //@use './ionic/ionic.functions.color';
1
+ @forward './ionic/ionic.functions.string';
2
+ @forward './ionic/ionic.functions.color';
3
3
 
4
4
  @function calculateRem($size) {
5
5
  $remSize: $size / 16px;
@@ -3,7 +3,7 @@
3
3
  // --------------------------------------------------
4
4
  // Import material icons
5
5
  // --------------------------------------------------
6
- @use 'material-design-icons-iconfont/src/material-design-icons' with (
6
+ @forward 'material-design-icons-iconfont/src/material-design-icons' with (
7
7
  $material-design-icons-font-directory-path: '../dist/fonts/'
8
8
  );
9
9
 
@@ -1,12 +1,13 @@
1
- @use 'ionic/ionic.globals' as *;
2
- @use 'ionic/ionic.theme.default' as *;
1
+ // Color functions (get-color-shade/tint, color-to-rgb-list) must be in scope here,
2
+ // otherwise the $colors map below (and the mixins in _mixins.scss) would emit them as
3
+ // literal, unevaluated CSS functions (e.g. `get-color-tint(#3880ff)`), producing invalid
4
+ // values for --ion-color-*-tint/-shade/-rgb. With Sass modules, `@use ... as *` is NOT
5
+ // transitive, so we must use/forward this module explicitly (the legacy @import made it global).
6
+ @use 'ionic/ionic.functions.color' as *;
3
7
 
4
8
  // --------------------------------------------------
5
9
  // Define Ionic defaults
6
10
  // --------------------------------------------------
7
- @forward 'ionic/ionic.globals';
8
- @forward 'ionic/ionic.theme.default';
9
-
10
11
  $ionicons-font-path: '~ionicons/dist/fonts';
11
12
 
12
13
  // --------------------------------------------------
@@ -140,4 +141,9 @@ $colors: (
140
141
  dark: (base: $dark, contrast: #fff, shade: get-color-shade($dark), tint: get-color-tint($dark)),
141
142
  /* transparent */
142
143
  transparent: (base: $transparent, contrast: $dark, shade: $transparent, tint: $transparent)
144
+ ) !default;
145
+
146
+ @forward 'ionic/ionic.functions.color';
147
+ @forward 'ionic/ionic.globals' with (
148
+ $colors: $colors
143
149
  );
@@ -80,3 +80,5 @@ $mat-toolbar-height-mobile: 56px !default;
80
80
  $mat-toolbar-background-color: var(--ion-toolbar-background, var(--ion-background-color, whitesmoke)) !default;
81
81
  $mat-option-min-height: 48px !default;
82
82
  $mat-option-max-height: 48px !default;
83
+ $mat-expansion-container-background-color: var(--ion-color-secondary100) !default;
84
+ $mat-expansion-container-text-color: var(--ion-text-color) !default;
@@ -4,8 +4,6 @@
4
4
  @use 'ngx-components.globals' as *;
5
5
  @use '@angular/material' as mat;
6
6
 
7
- @forward "functions";
8
-
9
7
  @mixin font-size($size) {
10
8
  font-size: calculateRem($size);
11
9
  }
@@ -44,12 +42,20 @@
44
42
  /**
45
43
  * Define CSS theme colors
46
44
  */
47
- @mixin generate-color($color-name) {
48
- $value: map-get($colors, $color-name);
45
+ @mixin generate-color($color-name, $value) {
49
46
  $base: map-get($value, base);
50
47
  $contrast: map-get($value, contrast);
48
+ @if not $contrast {
49
+ $contrast: get-color-contrast($base);
50
+ }
51
51
  $shade: map-get($value, shade);
52
+ @if not $shade {
53
+ $shade: get-color-shade($base);
54
+ }
52
55
  $tint: map-get($value, tint);
56
+ @if not $tint {
57
+ $tint: get-color-tint($base);
58
+ }
53
59
  --ion-color-base: var(--ion-color-#{""+$color-name}, #{$base}) !important;
54
60
  --ion-color-base-rgb: var(--ion-color-#{""+$color-name}-rgb, #{color-to-rgb-list($base)});
55
61
  --ion-color-contrast: var(--ion-color-#{""+$color-name}-contrast, #{$contrast}) !important;
@@ -58,10 +64,12 @@
58
64
  --ion-color-tint: var(--ion-color-#{""+$color-name}-tint, #{$tint}) !important;
59
65
  }
60
66
 
61
- @mixin generate-color-list($color-name) {
62
- $value: map-get($colors, $color-name);
67
+ @mixin generate-color-list($color-name, $value) {
63
68
  $base: map-get($value, base);
64
69
  $contrast: map-get($value, contrast);
70
+ @if not $contrast {
71
+ $contrast: get-color-contrast($base);
72
+ }
65
73
  $shade: map-get($value, shade);
66
74
  $tint: map-get($value, tint);
67
75
  --ion-color-#{""+$color-name} : #{$base};
@@ -72,8 +80,7 @@
72
80
  --ion-color-#{""+$color-name}-tint: #{$tint};
73
81
  }
74
82
 
75
- @mixin generate-dark-color-list($color-name) {
76
- $value: map-get($colors, $color-name);
83
+ @mixin generate-dark-color-list($color-name, $value) {
77
84
  $base: mix(map-get($value, base), #000, 50%);
78
85
  $contrast: get-color-contrast($base, true);
79
86
  $shade: lighten($base, 10%);
@@ -95,19 +102,19 @@
95
102
  }
96
103
  }
97
104
 
98
- @mixin css-variables-to-root() {
99
- @each $color-name,
100
- $value in $colors {
105
+ @mixin css-variables-to-root($colors) {
106
+ @each $color-name, $value in $colors {
101
107
  .ion-color-#{""+$color-name} {
102
- @include generate-color($color-name);
108
+ @include generate-color($color-name, $value);
103
109
  }
104
110
  }
105
111
 
106
112
  @each $color-name,
107
113
  $value in $colors {
108
- @include generate-color-list($color-name);
114
+ @include generate-color-list($color-name, $value);
109
115
  }
110
116
 
117
+
111
118
  --ion-text-color: #{$text-color-value};
112
119
  --ion-text-color-rgb: #{$text-color-rgb-value};
113
120
  --ion-grid-column-padding: #{$ion-grid-column-padding};
@@ -122,10 +129,10 @@
122
129
  --ion-backdrop-opacity: 0.32;
123
130
  --ion-card-background-color: #{$card-background-color};
124
131
 
125
- --expansion-panel-padding: var(--ion-padding);
126
- --expansion-panel-header-padding: var(--ion-padding);
127
- --mat-expansion-container-background-color: var(--ion-background-color);
128
- --mat-expansion-container-text-color: var(--ion-text-color);
132
+ --expansion-panel-padding: var($app-expansion-panel-padding, var(--ion-padding));
133
+ --expansion-panel-header-padding: var($app-expansion-panel-header-padding, var(--ion-padding));
134
+ --mat-expansion-container-background-color:var($mat-expansion-container-background-color, var(--ion-background-color));
135
+ --mat-expansion-container-text-color: var($mat-expansion-container-text-color, var(--ion-text-color));
129
136
 
130
137
  --mat-tab-link-height: #{$mat-tab-link-height};
131
138
  --mat-tab-bar-height: #{$mat-tab-bar-height};
@@ -193,7 +200,7 @@
193
200
 
194
201
  }
195
202
 
196
- @mixin css-variables-to-root-dark() {
203
+ @mixin css-variables-to-root-dark($colors) {
197
204
 
198
205
  @include mat.all-component-colors($candy-dark-theme);
199
206
 
@@ -202,7 +209,7 @@
202
209
 
203
210
  @each $color-name,
204
211
  $value in $colors {
205
- @include generate-dark-color-list($color-name);
212
+ @include generate-dark-color-list($color-name, $value);
206
213
  }
207
214
 
208
215
  // Inverse background/text color
@@ -13,6 +13,9 @@ $app-table-cell-focus-background-color: transparent !default;
13
13
  $app-paginator-height: 34px !default;
14
14
  $app-form-buttons-bar-height: 56px !default;
15
15
 
16
+ $app-expansion-panel-padding: 0 !default;
17
+ $app-expansion-panel-header-padding: 0 !default;
18
+
16
19
  // Form's field text and label
17
20
  $app-form-color: #{$text-color} !default;
18
21
  $app-form-color-rgb: #{$text-color-rgb} !default;
@@ -209,6 +209,12 @@ mat-option {
209
209
  padding: 0 !important;
210
210
  }
211
211
  }
212
+
213
+ /* hide pseudo checkbox, in the footer*/
214
+ &.mat-pseudo-checkbox-hidden mat-pseudo-checkbox {
215
+ visibility: hidden;
216
+ display: none;
217
+ }
212
218
  }
213
219
 
214
220
  mat-option:not(:last-of-type) {
@@ -296,7 +302,7 @@ mat-option:not(:last-of-type) {
296
302
  }
297
303
 
298
304
  &:has(.min-width-80vw),
299
- &:has(.mat-mdc-autocomplete-panel-80vw-size){
305
+ &:has(.mat-mdc-autocomplete-panel-80vw-size) {
300
306
  --min-width: 80vw;
301
307
  }
302
308
 
@@ -404,12 +410,14 @@ ion-list,
404
410
  /* Offset due to mat-pseudo-checkbox */
405
411
  padding-left: 32px;
406
412
  }
407
- &.column {
413
+ &.sticky {
408
414
  /* Set header as sticky*/
409
415
  position: -webkit-sticky;
410
416
  position: sticky;
411
- top: 0;
417
+ top: var(--top, 0);
412
418
  z-index: 1001;
419
+ }
420
+ &.column {
413
421
  background-color: var(--ion-color-secondary);
414
422
  border-bottom: 1px solid #{$app-select-option-border-color};
415
423
  color: var(--ion-color-secondary-contrast);
@@ -417,16 +425,16 @@ ion-list,
417
425
  @include font-size(12px);
418
426
  font-weight: 500;
419
427
  height: 20px;
428
+ --top: var(--header-column-top, 0);
420
429
  ion-col {
421
430
  white-space: nowrap;
422
431
  text-overflow: ellipsis;
423
432
  }
424
433
  &.mat-select-searchbar-sticky {
425
434
  /* offset because of the searchbar height */
426
- top: var(--ion-searchbar-height, 58px);
435
+ --header-column-top: var(--ion-searchbar-height, 58px);
427
436
  }
428
437
  }
429
-
430
438
  }
431
439
 
432
440
  ion-row.ion-list-footer,
@@ -437,7 +445,7 @@ ion-list,
437
445
  /* Set footer as sticky*/
438
446
  position: -webkit-sticky;
439
447
  position: sticky;
440
- bottom: 0;
448
+ bottom: var(--footer-bottom, 0);
441
449
  z-index: 1001;
442
450
  border-top: 1px solid #{$app-select-option-border-color};
443
451
  background-color: var(--ion-toolbar-background, #{$ion-toolbar-background});
@@ -453,6 +461,8 @@ ion-list,
453
461
  white-space: nowrap;
454
462
  text-overflow: ellipsis;
455
463
  }
464
+
465
+
456
466
  }
457
467
 
458
468
  ion-row {
@@ -14,6 +14,8 @@
14
14
  }
15
15
 
16
16
  .dark .mat-mdc-table {
17
+ // Pre-migration dark-theme sort arrow color (see light value in .mat-mdc-table below)
18
+ --mat-sort-arrow-color: #c6c6c6;
17
19
  --mat-checkbox-selected-checkmark-color: var(--ion-text-color);
18
20
  --mat-checkbox-unselected-icon-color: var(--ion-text-color);
19
21
  --mat-checkbox-selected-icon-color: var(--ion-color-accent);
@@ -28,6 +30,10 @@
28
30
  --mat-table-background-color: var(--background, var(--ion-background-color));
29
31
  --mat-table-header-color: var(--ion-color-primary);
30
32
  --mat-table-header-background-color: var(--mat-table-background-color);
33
+ // Restore the sort arrow color. The Angular Material M2 theme used to emit this token
34
+ // (#757575 light / #c6c6c6 dark); after the M3 migration it is no longer set, so the arrow
35
+ // falls back to --mat-sys-on-surface (black). Keep the pre-migration grey for consistency.
36
+ --mat-sort-arrow-color: #757575;
31
37
  --mat-table-row-item-label-text-color: var(--ion-text-color);
32
38
  --mat-table-row-item-outline-color: var(--ion-border-color);
33
39
  --mat-table-row-item-label-text-size: 14px;
@@ -120,6 +126,8 @@
120
126
  .mat-mdc-footer-cell {
121
127
  // Remove default padding
122
128
  padding-inline: 0;
129
+ padding-inline-start: 0; // Compat Chrome < 87 (padding-inline not exists yet)
130
+ padding-inline-end: 0; // Compat Chrome < 87 (padding-inline not exists yet)
123
131
 
124
132
  &.ion-padding-start {
125
133
  padding-inline-start: var(--mat-cell-horizontal-padding);
@@ -357,7 +365,10 @@
357
365
  }
358
366
 
359
367
  .mdc-text-field {
368
+ // Remove default padding
360
369
  padding-inline: 0;
370
+ padding-inline-start: 0; // Compat Chrome < 87 (padding-inline not exists yet)
371
+ padding-inline-end: 0; // Compat Chrome < 87 (padding-inline not exists yet)
361
372
  }
362
373
 
363
374
  // Prefix
@@ -1,21 +1,22 @@
1
- @use 'ionic.globals' as *;
2
- @use 'material.globals' as *;
3
- @use "ngx-components.globals" as *;
1
+ @forward 'ionic.globals';
2
+ @forward 'material.globals';
3
+ @forward 'ngx-components.globals';
4
4
 
5
5
  // --------------------------------------------------
6
6
  // Import Ionic style
7
7
  // --------------------------------------------------
8
8
  @use "ionic";
9
9
  // --------------------------------------------------
10
- // Import Ion Icons
10
+ // Import Icons
11
11
  // --------------------------------------------------
12
12
  $ionicons-font-path: '~ionicons/dist/fonts';
13
13
  @use "icons";
14
14
  // --------------------------------------------------
15
15
  // Import Fonts
16
16
  // --------------------------------------------------
17
- $roboto-font-path: '~roboto-fontface/fonts/roboto';
18
- @use "roboto";
17
+ @use "roboto" as * with (
18
+ $roboto-font-path: '~roboto-fontface/fonts/roboto'
19
+ );
19
20
  // --------------------------------------------------
20
21
  // Import responsive style
21
22
  // --------------------------------------------------
@@ -1,14 +1,20 @@
1
+ @use 'ionic.globals' as *;
1
2
  @use "mixins" as *;
2
3
 
4
+
3
5
  * {
4
6
  @include font-roboto();
5
7
  }
6
8
 
7
9
  /** Inject all CSS Variables **/
8
- html {
9
- @include css-variables-to-root();
10
-
11
- &.plt-mobile {
10
+ // NOTE: must use ':root' (not 'html') to match the specificity of Ionic 8's
11
+ // default palette, which is now declared on ':root' in @ionic/angular/css/core.css.
12
+ // A 'html' selector has a lower specificity than ':root' and would always be
13
+ // overridden by Ionic's defaults (e.g. --ion-color-primary: #0054e9).
14
+ :root {
15
+ @include css-variables-to-root($colors);
16
+
17
+ &:has(html.plt-mobile) {
12
18
  @include css-variables-to-root-mobile();
13
19
  }
14
20
 
@@ -19,7 +25,7 @@ html {
19
25
  * -------------------------------------------
20
26
  */
21
27
  html.dark {
22
- @include css-variables-to-root-dark();
28
+ @include css-variables-to-root-dark($colors);
23
29
 
24
30
  --ion-color-primary: #428cff;
25
31
  --ion-color-primary-rgb: 66, 140, 255;
@@ -8,8 +8,7 @@
8
8
  @use "./ionic.mixins" as *;
9
9
 
10
10
  // Default Theme
11
- @use "./ionic.theme.default" as *;
12
-
11
+ @forward "./ionic.theme.default";
13
12
 
14
13
  // Default General
15
14
  // --------------------------------------------------
@@ -0,0 +1,287 @@
1
+ import { Page, Locator, PlaywrightTestArgs } from '@playwright/test';
2
+
3
+ declare const STARTUP_DATA_STORAGE_KEY = "__appStartupData";
4
+
5
+ /**
6
+ * Utility selectors and helpers for Ionic components in e2e tests.
7
+ */
8
+ declare const IonicSelectors: {
9
+ /** Ionic button */
10
+ button: string;
11
+ /** Ionic modal */
12
+ modal: string;
13
+ /** Ionic content area */
14
+ content: string;
15
+ /** Ionic item */
16
+ item: string;
17
+ /** Ionic label */
18
+ label: string;
19
+ /** Ionic spinner */
20
+ spinner: string;
21
+ /** Ionic skeleton text (loading placeholder) */
22
+ skeletonText: string;
23
+ /** Ionic back button */
24
+ backButton: string;
25
+ /** Ionic toolbar */
26
+ toolbar: string;
27
+ /** Ionic footer */
28
+ footer: string;
29
+ /** Ionic header */
30
+ header: string;
31
+ };
32
+ /**
33
+ * Helper functions for interacting with Ionic components.
34
+ */
35
+ declare class IonicHelper {
36
+ private page;
37
+ constructor(page: Page);
38
+ /** Get an Ionic button by text content */
39
+ buttonByText(text: string | RegExp): Locator;
40
+ /** Get an Ionic button by CSS selector */
41
+ button(selector?: string, options?: {
42
+ hasText?: string | RegExp;
43
+ }): Locator;
44
+ /**
45
+ * Get an Ionic modal by its inner component selector (e.g. 'app-auth-form').
46
+ * Automatically wraps the selector in `ion-modal:has(...)` so callers
47
+ * only need to provide the component tag name.
48
+ */
49
+ modal(selector: string): Locator;
50
+ /** Wait for the Ionic app to be ready */
51
+ waitForAppReady(timeout?: number): Promise<void>;
52
+ /** Wait for loading spinners to disappear */
53
+ waitForLoadingComplete(timeout?: number): Promise<void>;
54
+ }
55
+
56
+ /**
57
+ * Utility selectors and helpers for Angular Material components in e2e tests.
58
+ */
59
+ declare const MaterialSelectors: {
60
+ /** Material table row */
61
+ row: string;
62
+ /** Material header row */
63
+ headerRow: string;
64
+ /** Material cell */
65
+ cell: string;
66
+ /** Material form field */
67
+ formField: string;
68
+ /** Material input */
69
+ input: string;
70
+ /** Material tab group */
71
+ tabGroup: string;
72
+ /** Material tab label */
73
+ tabLabel: string;
74
+ /** Material paginator */
75
+ paginator: string;
76
+ /** Material icon */
77
+ icon: string;
78
+ /** Material autocomplete panel */
79
+ autocompletePanel: string;
80
+ /** Material autocomplete option */
81
+ autocompleteOption: string;
82
+ };
83
+ /**
84
+ * Helper functions for interacting with Angular Material components.
85
+ */
86
+ declare class MaterialHelper {
87
+ private page;
88
+ constructor(page: Page);
89
+ /** Get all visible table rows, optionally scoped to a parent locator */
90
+ tableRows(scope?: Locator): Locator;
91
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
92
+ tableRow(index: number, scope?: Locator): Locator;
93
+ /** Click on a table row by index (0-based), optionally scoped to a parent locator */
94
+ clickRow(index: number, scope?: Locator): Promise<void>;
95
+ /** Get a tab by its label text */
96
+ tabByLabel(text: string | RegExp): Locator;
97
+ /** Click a tab by its label text */
98
+ clickTab(text: string | RegExp): Promise<void>;
99
+ /** Fill a mat-form-field input identified by its formControlName.
100
+ * Supports both native inputs with formcontrolname and custom components (e.g. mat-autocomplete-field) wrapping an input. */
101
+ fillFormControl(formControlName: string, value: string): Promise<void>;
102
+ /**
103
+ * Select an option from a mat-select dropdown by formControlName.
104
+ * Opens the dropdown, waits for options to render, clicks the matching option, then waits for the dropdown to close.
105
+ */
106
+ selectMatSelectOption(formControlName: string, optionText: string | RegExp): Promise<void>;
107
+ /** Select an autocomplete option by text after typing in a form control */
108
+ selectAutocompleteOption(formControlName: string, searchText: string, optionText: string | RegExp): Promise<void>;
109
+ /** Wait for table rows to be loaded (at least one row visible), optionally scoped to a parent locator */
110
+ waitForTableRows(timeout?: number, scope?: Locator): Promise<void>;
111
+ }
112
+
113
+ /**
114
+ * Common UI helper functions to improve readability of e2e tests.
115
+ * This helper centralizes access to Ionic and Material specific helpers.
116
+ */
117
+ declare class UiHelper {
118
+ protected page: Page;
119
+ protected ionic: IonicHelper;
120
+ protected mat: MaterialHelper;
121
+ constructor(page: Page);
122
+ /**
123
+ * Click an element and wait for it to be ready.
124
+ * @param locator The locator to click.
125
+ * @param timeout The maximum time to wait.
126
+ */
127
+ click(locator: Locator, timeout?: number): Promise<void>;
128
+ /**
129
+ * Fill an input and wait for it to be ready.
130
+ * @param locator The locator to fill.
131
+ * @param value The value to enter.
132
+ * @param timeout The maximum time to wait.
133
+ */
134
+ fill(locator: Locator, value: string, timeout?: number): Promise<void>;
135
+ /**
136
+ * Wait for an element to be visible.
137
+ * @param locator The locator to wait for.
138
+ * @param timeout The maximum time to wait.
139
+ */
140
+ waitForVisible(locator: Locator, timeout?: number): Promise<void>;
141
+ /**
142
+ * Wait for an element to be hidden.
143
+ * @param locator The locator to wait for.
144
+ * @param timeout The maximum time to wait.
145
+ */
146
+ waitForHidden(locator: Locator, timeout?: number): Promise<void>;
147
+ /**
148
+ * Get a button by its text content.
149
+ * Supports both Ionic and Material buttons.
150
+ * @param text The text content to match.
151
+ * @param scope Optional locator to scope the search within.
152
+ */
153
+ button(text: string | RegExp, scope?: Locator): Locator;
154
+ /**
155
+ * Get an icon button by its icon name.
156
+ * Supports both Ionic and Material icon buttons.
157
+ * @param iconName The icon name (e.g. 'mail', 'search', 'ellipsis-horizontal').
158
+ * @param scope Optional locator to scope the search within.
159
+ */
160
+ iconButton(iconName: string, scope?: Locator): Locator;
161
+ /**
162
+ * Access to the underlying Playwright Page object.
163
+ */
164
+ get pageObj(): Page;
165
+ /** Get a modal by its selector */
166
+ modal(selector: string): Locator;
167
+ /** Get a tab by its label text */
168
+ tab(text: string | RegExp): Locator;
169
+ /** Wait for the app to be ready */
170
+ waitForAppReady(timeout?: number): Promise<void>;
171
+ /** Wait for loading spinners to disappear */
172
+ waitForLoadingComplete(timeout?: number): Promise<void>;
173
+ /** Wait for table rows to be loaded, optionally scoped to a parent locator */
174
+ waitForTableRows(timeout?: number, scope?: Locator): Promise<void>;
175
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
176
+ tableRow(index: number, scope?: Locator): Locator;
177
+ /** Click on a table row by index, optionally scoped to a parent locator */
178
+ clickRow(index: number, scope?: Locator): Promise<void>;
179
+ /** Fill a mat-form-field input identified by its formControlName */
180
+ fillFormControl(formControlName: string, value: string): Promise<void>;
181
+ /** Select an autocomplete option */
182
+ selectAutocompleteOption(formControlName: string, searchText: string, optionText: string | RegExp): Promise<void>;
183
+ /**
184
+ * Select an option from a mat-select dropdown.
185
+ * Opens the dropdown, waits for options, clicks the matching one, then waits for the dropdown to close.
186
+ */
187
+ selectOption(formControlName: string, optionText: string | RegExp): Promise<void>;
188
+ }
189
+
190
+ /**
191
+ * Helper for authentication in e2e tests.
192
+ */
193
+ declare class AuthHelper {
194
+ private page;
195
+ protected ui: UiHelper;
196
+ constructor(page: Page, ui?: UiHelper);
197
+ /** Login with given test credentials */
198
+ login(username: string, password: string): Promise<void>;
199
+ /** Wait until the user is logged in and return the email shown in the side menu */
200
+ waitForLoggedIn(): Promise<string | null>;
201
+ /** Setup startup data with provided credentials or environment variables */
202
+ loginByEnv(opts?: {
203
+ username?: string;
204
+ password?: string;
205
+ offline?: boolean;
206
+ peerUrl?: string;
207
+ }): Promise<void>;
208
+ }
209
+
210
+ /**
211
+ * Search environments based on provided criteria.
212
+ *
213
+ * Use when you need to run tests with one or more environments.
214
+ * Test can use `testWithEnv` to run tests with a specific environment.
215
+ * Example :
216
+ *
217
+ * ```
218
+ * test.describe('Sample test with environment', () => {
219
+ * withEnvs({
220
+ * peerTags: ['local'],
221
+ * userTags: ['admin'],
222
+ * }).forEach((env) => {
223
+ * testWithEnv(env)(`Some test with environment ${env.id}`, async ({ page }) => {
224
+ * // Test implementation
225
+ * });
226
+ * });
227
+ * });
228
+ * ```
229
+ *
230
+ * Environments are loaded from a JSON config file located at one of four paths :
231
+ * - `LOGIN_ENVS_PATH` environment variable
232
+ * - `.environments.json` in the current directory (e.g. `.environments.json`)
233
+ * - `.environments.json` in the `.local` directory (e.g. `.local/environments.json`)
234
+ * - `.environments.json` in the `e2e` directory (e.g. `e2e/.environments.json`)
235
+ *
236
+ * Example of JSON :
237
+ *
238
+ * ```
239
+ * {
240
+ * "peers": [
241
+ * {
242
+ * "id": "local",
243
+ * "tags": ["local"],
244
+ * "url": "http://localhost:8080",
245
+ * "users": [
246
+ * {
247
+ * "tags": ["admin"],
248
+ * "username": "admin@sumaris.net",
249
+ * "password": "admin"
250
+ * }
251
+ * ]
252
+ * }
253
+ * ]
254
+ * }
255
+ * ```
256
+ *
257
+ * @see testWithEnv
258
+ * @param filter Search criteria.
259
+ */
260
+ declare function withEnvs(filter: EnvFilter): LoginEnv[];
261
+ /**
262
+ * Currying function that returns a test function that can be used to run tests with a specific environment.
263
+ *
264
+ * ```
265
+ * testWithEnv(env)(`Some test with environment ${env.id}`, async ({ page }) => {
266
+ * // Test implementation
267
+ * })
268
+ * ```
269
+ *
270
+ * @see withEnvs
271
+ * @param env Environment to use for the test.
272
+ */
273
+ declare function testWithEnv(env: LoginEnv): (title: string, testFn: (args: PlaywrightTestArgs) => Promise<void>) => void;
274
+ interface EnvFilter {
275
+ peerId?: string;
276
+ peerTags?: string[];
277
+ userTags?: string[];
278
+ }
279
+ interface LoginEnv {
280
+ id?: string;
281
+ peerUrl: string;
282
+ username: string;
283
+ password: string;
284
+ }
285
+
286
+ export { AuthHelper, IonicHelper, IonicSelectors, MaterialHelper, MaterialSelectors, STARTUP_DATA_STORAGE_KEY, UiHelper, testWithEnv, withEnvs };
287
+ export type { EnvFilter, LoginEnv };