@sumaris-net/ngx-components 18.24.24 → 18.25.1

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 (39) hide show
  1. package/doc/changelog.md +10 -0
  2. package/esm2022/public_api.mjs +5 -1
  3. package/esm2022/src/app/core/services/account.service.mjs +33 -10
  4. package/esm2022/src/app/core/services/platform.service.mjs +3 -3
  5. package/esm2022/src/app/core/services/startup.constants.mjs +10 -0
  6. package/esm2022/src/app/core/services/startup.model.mjs +2 -0
  7. package/esm2022/src/app/core/services/startup.service.mjs +148 -0
  8. package/esm2022/src/app/shared/shared.module.mjs +2 -2
  9. package/esm2022/src/app/shared/storage/storage.service.mjs +2 -3
  10. package/esm2022/src/environments/environment.class.mjs +4 -1
  11. package/esm2022/src/environments/environment.mjs +2 -1
  12. package/esm2022/testing/sumaris-net-ngx-components-testing.mjs +5 -0
  13. package/esm2022/testing/test/login-env.mjs +122 -0
  14. package/esm2022/testing/testing_api.mjs +10 -0
  15. package/esm2022/testing/utils/auth.utils.mjs +64 -0
  16. package/esm2022/testing/utils/ionic.utils.mjs +66 -0
  17. package/esm2022/testing/utils/material.utils.mjs +100 -0
  18. package/esm2022/testing/utils/startup.constants.mjs +4 -0
  19. package/esm2022/testing/utils/ui.utils.mjs +139 -0
  20. package/fesm2022/sumaris-net-ngx-components-testing.mjs +499 -0
  21. package/fesm2022/sumaris-net-ngx-components-testing.mjs.map +1 -0
  22. package/fesm2022/sumaris-net.ngx-components.mjs +315 -140
  23. package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
  24. package/package.json +8 -1
  25. package/public_api.d.ts +3 -0
  26. package/src/app/core/services/account.service.d.ts +5 -2
  27. package/src/app/core/services/startup.constants.d.ts +1 -0
  28. package/src/app/core/services/startup.model.d.ts +1 -0
  29. package/src/app/core/services/startup.service.d.ts +31 -0
  30. package/src/assets/manifest.json +1 -1
  31. package/src/environments/environment.class.d.ts +2 -0
  32. package/testing/index.d.ts +5 -0
  33. package/testing/test/login-env.d.ts +76 -0
  34. package/testing/testing_api.d.ts +6 -0
  35. package/testing/utils/auth.utils.d.ts +21 -0
  36. package/testing/utils/ionic.utils.d.ts +51 -0
  37. package/testing/utils/material.utils.d.ts +57 -0
  38. package/testing/utils/startup.constants.d.ts +1 -0
  39. package/testing/utils/ui.utils.d.ts +79 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sumaris-net/ngx-components",
3
3
  "description": "SUMARiS Angular components",
4
- "version": "18.24.24",
4
+ "version": "18.25.1",
5
5
  "author": "contact@e-is.pro",
6
6
  "license": "AGPL-3.0",
7
7
  "readmeFilename": "README.md",
@@ -32,6 +32,7 @@
32
32
  "peerDependencies": {
33
33
  "@apollo/client": "~3.12.4",
34
34
  "@ionic/storage": "~4.0.0",
35
+ "@playwright/test": "^1.58.2",
35
36
  "d3": "~7.6.1",
36
37
  "localforage": "~1.10.0",
37
38
  "rxjs": "~7.8.2",
@@ -92,6 +93,12 @@
92
93
  "esm2022": "./esm2022/sumaris-net.ngx-components.mjs",
93
94
  "esm": "./esm2022/sumaris-net.ngx-components.mjs",
94
95
  "default": "./fesm2022/sumaris-net.ngx-components.mjs"
96
+ },
97
+ "./testing": {
98
+ "types": "./testing/index.d.ts",
99
+ "esm2022": "./esm2022/testing/sumaris-net-ngx-components-testing.mjs",
100
+ "esm": "./esm2022/testing/sumaris-net-ngx-components-testing.mjs",
101
+ "default": "./fesm2022/sumaris-net-ngx-components-testing.mjs"
95
102
  }
96
103
  },
97
104
  "sideEffects": false
package/public_api.d.ts CHANGED
@@ -199,6 +199,9 @@ export * from './src/app/core/services/base-entity-service.class';
199
199
  export * from './src/app/core/services/storage/entities-storage.service';
200
200
  export * from './src/app/core/services/storage/entity-store.class';
201
201
  export * from './src/app/core/services/validator/base.validator.class';
202
+ export * from './src/app/core/services/startup.model';
203
+ export { STARTUP_DATA_STORAGE_KEY } from './src/app/core/services/startup.constants';
204
+ export { StartupService, StartupData, createAppStartupInitializer } from './src/app/core/services/startup.service';
202
205
  export * from './src/app/core/form/form.class';
203
206
  export * from './src/app/core/form/form.utils';
204
207
  export * from './src/app/core/form/form-container.class';
@@ -22,6 +22,7 @@ import { OverlayEventDetail } from '@ionic/core';
22
22
  import { ToastController } from '@ionic/angular';
23
23
  import { TranslateService } from '@ngx-translate/core';
24
24
  import { TokenScope, UserToken } from './model/token.model';
25
+ import { StartupService } from './startup.service';
25
26
  import * as i0 from "@angular/core";
26
27
  export declare interface AccountDetails {
27
28
  loaded: boolean;
@@ -87,6 +88,7 @@ export declare class AccountService extends BaseGraphqlService<Account, any, num
87
88
  protected settings: LocalSettingsService;
88
89
  protected storage: Storage;
89
90
  protected file: FileService;
91
+ protected startupService: StartupService;
90
92
  private translate;
91
93
  private toastController;
92
94
  protected environment: Environment;
@@ -118,7 +120,7 @@ export declare class AccountService extends BaseGraphqlService<Account, any, num
118
120
  get apiTokenEnabled(): boolean;
119
121
  set apiTokenEnabled(value: boolean);
120
122
  get remoteLocalSettingsKeys(): string[];
121
- constructor(network: NetworkService, graphql: GraphqlService, settings: LocalSettingsService, storage: Storage, file: FileService, translate: TranslateService, toastController: ToastController, environment: Environment, options: UserSettingsOptions, accountOptions?: AccountServiceOptions);
123
+ constructor(network: NetworkService, graphql: GraphqlService, settings: LocalSettingsService, storage: Storage, file: FileService, startupService: StartupService, translate: TranslateService, toastController: ToastController, environment: Environment, options: UserSettingsOptions, accountOptions?: AccountServiceOptions);
122
124
  ngOnStart(): Promise<Account>;
123
125
  protected ngOnStop(): Promise<void>;
124
126
  isLogin(): boolean;
@@ -201,6 +203,7 @@ export declare class AccountService extends BaseGraphqlService<Account, any, num
201
203
  fetchPolicy?: FetchPolicy;
202
204
  }): Promise<Account>;
203
205
  protected listenSettings(): Promise<void>;
206
+ protected autoLogin(): Promise<boolean>;
204
207
  protected restoreLocally(): Promise<Account | undefined>;
205
208
  /**
206
209
  * Save account into the local storage
@@ -229,7 +232,7 @@ export declare class AccountService extends BaseGraphqlService<Account, any, num
229
232
  protected copyIdAndUpdateDateSettings(source: UserSettings | undefined, target: UserSettings): void;
230
233
  protected copyIdAndUpdateDateToken(sources: UserToken[] | undefined, targets: UserToken[]): void;
231
234
  protected showToast<T = any>(opts: ShowToastOptions): Promise<OverlayEventDetail<T>>;
232
- static ɵfac: i0.ɵɵFactoryDeclaration<AccountService, [null, null, null, null, null, { optional: true; }, { optional: true; }, null, { optional: true; }, { optional: true; }]>;
235
+ static ɵfac: i0.ɵɵFactoryDeclaration<AccountService, [null, null, null, null, null, { optional: true; }, { optional: true; }, { optional: true; }, null, { optional: true; }, { optional: true; }]>;
233
236
  static ɵprov: i0.ɵɵInjectableDeclaration<AccountService>;
234
237
  }
235
238
  export declare function provideAccountService(): Provider;
@@ -0,0 +1 @@
1
+ export declare const STARTUP_DATA_STORAGE_KEY = "__appStartupData";
@@ -0,0 +1 @@
1
+ export type StartupMethod = 'url' | 'window' | 'environment';
@@ -0,0 +1,31 @@
1
+ import { AuthData } from './account.service';
2
+ import { StorageService } from '../../shared/storage/storage.service';
3
+ import { STARTUP_DATA_STORAGE_KEY } from './startup.constants';
4
+ import * as i0 from "@angular/core";
5
+ export { STARTUP_DATA_STORAGE_KEY };
6
+ export interface StartupData extends AuthData {
7
+ peerUrl?: string;
8
+ }
9
+ export declare class StartupService {
10
+ private _data;
11
+ protected storage: StorageService;
12
+ protected environment: import("../../../environments/environment.class").Environment;
13
+ get data(): StartupData;
14
+ set data(data: StartupData);
15
+ hasPeer(): boolean;
16
+ consumePeer(): string | null;
17
+ hasAuth(): boolean;
18
+ consumeAuth(): AuthData | null;
19
+ start(): Promise<void>;
20
+ static ɵfac: i0.ɵɵFactoryDeclaration<StartupService, never>;
21
+ static ɵprov: i0.ɵɵInjectableDeclaration<StartupService>;
22
+ }
23
+ /**
24
+ * Factory function for the app startup initializer.
25
+ * In Angular 18, this is used with APP_INITIALIZER:
26
+ * { provide: APP_INITIALIZER, useFactory: createAppStartupInitializer, multi: true }
27
+ *
28
+ * inject() is called during factory execution (in injection context), then the
29
+ * returned function calls the already-injected service.
30
+ */
31
+ export declare function createAppStartupInitializer(): () => Promise<void>;
@@ -2,7 +2,7 @@
2
2
  "name": "ngx-sumaris-components",
3
3
  "short_name": "ngx-sumaris-components",
4
4
  "manifest_version": 1,
5
- "version": "18.24.24",
5
+ "version": "18.25.1",
6
6
  "default_locale": "fr",
7
7
  "description": "Angular components for building beautiful and responsive Apps",
8
8
  "icons": [{
@@ -3,6 +3,7 @@ import { StorageConfig } from '@ionic/storage';
3
3
  import { InjectionToken } from '@angular/core';
4
4
  import { CacheConfig } from 'ionic-cache/lib/interfaces/cache-config.interface';
5
5
  import { LoggingServiceConfig } from '../app/shared/logging/logging-service.config';
6
+ import { StartupMethod } from '../app/core/services/startup.model';
6
7
  export declare const ENVIRONMENT: InjectionToken<Environment>;
7
8
  export declare class Environment {
8
9
  name: string;
@@ -65,6 +66,7 @@ export declare class Environment {
65
66
  password?: string;
66
67
  token?: string;
67
68
  };
69
+ startupOrder?: StartupMethod[];
68
70
  account?: {
69
71
  showAccountButton?: boolean;
70
72
  enableListenChanges?: boolean;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Generated bundle index. Do not edit.
3
+ */
4
+ /// <amd-module name="@sumaris-net/ngx-components/testing" />
5
+ export * from './testing_api';
@@ -0,0 +1,76 @@
1
+ import { PlaywrightTestArgs } from '@playwright/test';
2
+ /**
3
+ * Search environments based on provided criteria.
4
+ *
5
+ * Use when you need to run tests with one or more environments.
6
+ * Test can use `testWithEnv` to run tests with a specific environment.
7
+ * Example :
8
+ *
9
+ * ```
10
+ * test.describe('Sample test with environment', () => {
11
+ * withEnvs({
12
+ * peerTags: ['local'],
13
+ * userTags: ['admin'],
14
+ * }).forEach((env) => {
15
+ * testWithEnv(env)(`Some test with environment ${env.id}`, async ({ page }) => {
16
+ * // Test implementation
17
+ * });
18
+ * });
19
+ * });
20
+ * ```
21
+ *
22
+ * Environments are loaded from a JSON config file located at one of four paths :
23
+ * - `LOGIN_ENVS_PATH` environment variable
24
+ * - `.environments.json` in the current directory (e.g. `.environments.json`)
25
+ * - `.environments.json` in the `.local` directory (e.g. `.local/environments.json`)
26
+ * - `.environments.json` in the `e2e` directory (e.g. `e2e/.environments.json`)
27
+ *
28
+ * Example of JSON :
29
+ *
30
+ * ```
31
+ * {
32
+ * "peers": [
33
+ * {
34
+ * "id": "local",
35
+ * "tags": ["local"],
36
+ * "url": "http://localhost:8080",
37
+ * "users": [
38
+ * {
39
+ * "tags": ["admin"],
40
+ * "username": "admin@sumaris.net",
41
+ * "password": "***"
42
+ * }
43
+ * ]
44
+ * }
45
+ * ]
46
+ * }
47
+ * ```
48
+ *
49
+ * @see testWithEnv
50
+ * @param filter Search criteria.
51
+ */
52
+ export declare function withEnvs(filter: EnvFilter): LoginEnv[];
53
+ /**
54
+ * Currying function that returns a test function that can be used to run tests with a specific environment.
55
+ *
56
+ * ```
57
+ * testWithEnv(env)(`Some test with environment ${env.id}`, async ({ page }) => {
58
+ * // Test implementation
59
+ * })
60
+ * ```
61
+ *
62
+ * @see withEnvs
63
+ * @param env Environment to use for the test.
64
+ */
65
+ export declare function testWithEnv(env: LoginEnv): (title: string, testFn: (args: PlaywrightTestArgs) => Promise<void>) => void;
66
+ export interface EnvFilter {
67
+ peerId?: string;
68
+ peerTags?: string[];
69
+ userTags?: string[];
70
+ }
71
+ export interface LoginEnv {
72
+ id?: string;
73
+ peerUrl: string;
74
+ username: string;
75
+ password: string;
76
+ }
@@ -0,0 +1,6 @@
1
+ export * from './utils/startup.constants';
2
+ export * from './utils/auth.utils';
3
+ export * from './utils/ionic.utils';
4
+ export * from './utils/material.utils';
5
+ export * from './utils/ui.utils';
6
+ export * from './test/login-env';
@@ -0,0 +1,21 @@
1
+ import { Page } from 'playwright';
2
+ import { UiHelper } from './ui.utils';
3
+ /**
4
+ * Helper for authentication in e2e tests.
5
+ */
6
+ export declare class AuthHelper {
7
+ private page;
8
+ protected ui: UiHelper;
9
+ constructor(page: Page, ui?: UiHelper);
10
+ /** Login with given test credentials */
11
+ login(username: string, password: string): Promise<void>;
12
+ /** Wait until the user is logged in and return the email shown in the side menu */
13
+ waitForLoggedIn(): Promise<string | null>;
14
+ /** Setup startup data with provided credentials or environment variables */
15
+ loginByEnv(opts?: {
16
+ username?: string;
17
+ password?: string;
18
+ offline?: boolean;
19
+ peerUrl?: string;
20
+ }): Promise<void>;
21
+ }
@@ -0,0 +1,51 @@
1
+ import { Locator, Page } from 'playwright';
2
+ /**
3
+ * Utility selectors and helpers for Ionic components in e2e tests.
4
+ */
5
+ export declare const IonicSelectors: {
6
+ /** Ionic button */
7
+ button: string;
8
+ /** Ionic modal */
9
+ modal: string;
10
+ /** Ionic content area */
11
+ content: string;
12
+ /** Ionic item */
13
+ item: string;
14
+ /** Ionic label */
15
+ label: string;
16
+ /** Ionic spinner */
17
+ spinner: string;
18
+ /** Ionic skeleton text (loading placeholder) */
19
+ skeletonText: string;
20
+ /** Ionic back button */
21
+ backButton: string;
22
+ /** Ionic toolbar */
23
+ toolbar: string;
24
+ /** Ionic footer */
25
+ footer: string;
26
+ /** Ionic header */
27
+ header: string;
28
+ };
29
+ /**
30
+ * Helper functions for interacting with Ionic components.
31
+ */
32
+ export declare class IonicHelper {
33
+ private page;
34
+ constructor(page: Page);
35
+ /** Get an Ionic button by text content */
36
+ buttonByText(text: string | RegExp): Locator;
37
+ /** Get an Ionic button by CSS selector */
38
+ button(selector?: string, options?: {
39
+ hasText?: string | RegExp;
40
+ }): Locator;
41
+ /**
42
+ * Get an Ionic modal by its inner component selector (e.g. 'app-auth-form').
43
+ * Automatically wraps the selector in `ion-modal:has(...)` so callers
44
+ * only need to provide the component tag name.
45
+ */
46
+ modal(selector: string): Locator;
47
+ /** Wait for the Ionic app to be ready */
48
+ waitForAppReady(timeout?: number): Promise<void>;
49
+ /** Wait for loading spinners to disappear */
50
+ waitForLoadingComplete(timeout?: number): Promise<void>;
51
+ }
@@ -0,0 +1,57 @@
1
+ import { Locator, Page } from 'playwright';
2
+ /**
3
+ * Utility selectors and helpers for Angular Material components in e2e tests.
4
+ */
5
+ export declare const MaterialSelectors: {
6
+ /** Material table row */
7
+ row: string;
8
+ /** Material header row */
9
+ headerRow: string;
10
+ /** Material cell */
11
+ cell: string;
12
+ /** Material form field */
13
+ formField: string;
14
+ /** Material input */
15
+ input: string;
16
+ /** Material tab group */
17
+ tabGroup: string;
18
+ /** Material tab label */
19
+ tabLabel: string;
20
+ /** Material paginator */
21
+ paginator: string;
22
+ /** Material icon */
23
+ icon: string;
24
+ /** Material autocomplete panel */
25
+ autocompletePanel: string;
26
+ /** Material autocomplete option */
27
+ autocompleteOption: string;
28
+ };
29
+ /**
30
+ * Helper functions for interacting with Angular Material components.
31
+ */
32
+ export declare class MaterialHelper {
33
+ private page;
34
+ constructor(page: Page);
35
+ /** Get all visible table rows, optionally scoped to a parent locator */
36
+ tableRows(scope?: Locator): Locator;
37
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
38
+ tableRow(index: number, scope?: Locator): Locator;
39
+ /** Click on a table row by index (0-based), optionally scoped to a parent locator */
40
+ clickRow(index: number, scope?: Locator): Promise<void>;
41
+ /** Get a tab by its label text */
42
+ tabByLabel(text: string | RegExp): Locator;
43
+ /** Click a tab by its label text */
44
+ clickTab(text: string | RegExp): Promise<void>;
45
+ /** Fill a mat-form-field input identified by its formControlName.
46
+ * Supports both native inputs with formcontrolname and custom components (e.g. mat-autocomplete-field) wrapping an input. */
47
+ fillFormControl(formControlName: string, value: string): Promise<void>;
48
+ /**
49
+ * Select an option from a mat-select dropdown by formControlName.
50
+ * Opens the dropdown, waits for options to render, clicks the matching option, then waits for the dropdown to close.
51
+ */
52
+ selectMatSelectOption(formControlName: string, optionText: string | RegExp): Promise<void>;
53
+ /** Select an autocomplete option by text after typing in a form control */
54
+ selectAutocompleteOption(formControlName: string, searchText: string, optionText: string | RegExp): Promise<void>;
55
+ /** Wait for table rows to be loaded (at least one row visible), optionally scoped to a parent locator */
56
+ waitForTableRows(timeout?: number, scope?: Locator): Promise<void>;
57
+ }
@@ -0,0 +1 @@
1
+ export declare const STARTUP_DATA_STORAGE_KEY = "__appStartupData";
@@ -0,0 +1,79 @@
1
+ import { Locator, Page } from 'playwright';
2
+ import { IonicHelper } from './ionic.utils';
3
+ import { MaterialHelper } from './material.utils';
4
+ /**
5
+ * Common UI helper functions to improve readability of e2e tests.
6
+ * This helper centralizes access to Ionic and Material specific helpers.
7
+ */
8
+ export declare class UiHelper {
9
+ protected page: Page;
10
+ protected ionic: IonicHelper;
11
+ protected mat: MaterialHelper;
12
+ constructor(page: Page);
13
+ /**
14
+ * Click an element and wait for it to be ready.
15
+ * @param locator The locator to click.
16
+ * @param timeout The maximum time to wait.
17
+ */
18
+ click(locator: Locator, timeout?: number): Promise<void>;
19
+ /**
20
+ * Fill an input and wait for it to be ready.
21
+ * @param locator The locator to fill.
22
+ * @param value The value to enter.
23
+ * @param timeout The maximum time to wait.
24
+ */
25
+ fill(locator: Locator, value: string, timeout?: number): Promise<void>;
26
+ /**
27
+ * Wait for an element to be visible.
28
+ * @param locator The locator to wait for.
29
+ * @param timeout The maximum time to wait.
30
+ */
31
+ waitForVisible(locator: Locator, timeout?: number): Promise<void>;
32
+ /**
33
+ * Wait for an element to be hidden.
34
+ * @param locator The locator to wait for.
35
+ * @param timeout The maximum time to wait.
36
+ */
37
+ waitForHidden(locator: Locator, timeout?: number): Promise<void>;
38
+ /**
39
+ * Get a button by its text content.
40
+ * Supports both Ionic and Material buttons.
41
+ * @param text The text content to match.
42
+ * @param scope Optional locator to scope the search within.
43
+ */
44
+ button(text: string | RegExp, scope?: Locator): Locator;
45
+ /**
46
+ * Get an icon button by its icon name.
47
+ * Supports both Ionic and Material icon buttons.
48
+ * @param iconName The icon name (e.g. 'mail', 'search', 'ellipsis-horizontal').
49
+ * @param scope Optional locator to scope the search within.
50
+ */
51
+ iconButton(iconName: string, scope?: Locator): Locator;
52
+ /**
53
+ * Access to the underlying Playwright Page object.
54
+ */
55
+ get pageObj(): Page;
56
+ /** Get a modal by its selector */
57
+ modal(selector: string): Locator;
58
+ /** Get a tab by its label text */
59
+ tab(text: string | RegExp): Locator;
60
+ /** Wait for the app to be ready */
61
+ waitForAppReady(timeout?: number): Promise<void>;
62
+ /** Wait for loading spinners to disappear */
63
+ waitForLoadingComplete(timeout?: number): Promise<void>;
64
+ /** Wait for table rows to be loaded, optionally scoped to a parent locator */
65
+ waitForTableRows(timeout?: number, scope?: Locator): Promise<void>;
66
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
67
+ tableRow(index: number, scope?: Locator): Locator;
68
+ /** Click on a table row by index, optionally scoped to a parent locator */
69
+ clickRow(index: number, scope?: Locator): Promise<void>;
70
+ /** Fill a mat-form-field input identified by its formControlName */
71
+ fillFormControl(formControlName: string, value: string): Promise<void>;
72
+ /** Select an autocomplete option */
73
+ selectAutocompleteOption(formControlName: string, searchText: string, optionText: string | RegExp): Promise<void>;
74
+ /**
75
+ * Select an option from a mat-select dropdown.
76
+ * Opens the dropdown, waits for options, clicks the matching one, then waits for the dropdown to close.
77
+ */
78
+ selectOption(formControlName: string, optionText: string | RegExp): Promise<void>;
79
+ }