@sumaris-net/ngx-components 18.24.24 → 18.25.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.
Files changed (37) hide show
  1. package/doc/changelog.md +7 -0
  2. package/esm2022/src/app/core/services/account.service.mjs +33 -10
  3. package/esm2022/src/app/core/services/platform.service.mjs +3 -3
  4. package/esm2022/src/app/core/services/startup.constants.mjs +10 -0
  5. package/esm2022/src/app/core/services/startup.model.mjs +2 -0
  6. package/esm2022/src/app/core/services/startup.service.mjs +148 -0
  7. package/esm2022/src/app/shared/shared.module.mjs +2 -2
  8. package/esm2022/src/app/shared/storage/storage.service.mjs +2 -3
  9. package/esm2022/src/environments/environment.class.mjs +4 -1
  10. package/esm2022/src/environments/environment.mjs +2 -1
  11. package/esm2022/testing/sumaris-net-ngx-components-testing.mjs +5 -0
  12. package/esm2022/testing/test/login-env.mjs +122 -0
  13. package/esm2022/testing/testing_api.mjs +10 -0
  14. package/esm2022/testing/utils/auth.utils.mjs +64 -0
  15. package/esm2022/testing/utils/ionic.utils.mjs +66 -0
  16. package/esm2022/testing/utils/material.utils.mjs +100 -0
  17. package/esm2022/testing/utils/startup.constants.mjs +4 -0
  18. package/esm2022/testing/utils/ui.utils.mjs +139 -0
  19. package/fesm2022/sumaris-net-ngx-components-testing.mjs +499 -0
  20. package/fesm2022/sumaris-net-ngx-components-testing.mjs.map +1 -0
  21. package/fesm2022/sumaris-net.ngx-components.mjs +314 -139
  22. package/fesm2022/sumaris-net.ngx-components.mjs.map +1 -1
  23. package/package.json +8 -1
  24. package/src/app/core/services/account.service.d.ts +5 -2
  25. package/src/app/core/services/startup.constants.d.ts +1 -0
  26. package/src/app/core/services/startup.model.d.ts +1 -0
  27. package/src/app/core/services/startup.service.d.ts +31 -0
  28. package/src/assets/manifest.json +1 -1
  29. package/src/environments/environment.class.d.ts +2 -0
  30. package/testing/index.d.ts +5 -0
  31. package/testing/test/login-env.d.ts +76 -0
  32. package/testing/testing_api.d.ts +6 -0
  33. package/testing/utils/auth.utils.d.ts +21 -0
  34. package/testing/utils/ionic.utils.d.ts +51 -0
  35. package/testing/utils/material.utils.d.ts +57 -0
  36. package/testing/utils/startup.constants.d.ts +1 -0
  37. package/testing/utils/ui.utils.d.ts +79 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Utility selectors and helpers for Angular Material components in e2e tests.
3
+ */
4
+ export const MaterialSelectors = {
5
+ /** Material table row */
6
+ row: 'mat-row, tr[mat-row]',
7
+ /** Material header row */
8
+ headerRow: 'mat-header-row, tr[mat-header-row]',
9
+ /** Material cell */
10
+ cell: 'mat-cell, td[mat-cell]',
11
+ /** Material form field */
12
+ formField: 'mat-form-field',
13
+ /** Material input */
14
+ input: 'input[matInput]',
15
+ /** Material tab group */
16
+ tabGroup: 'mat-tab-group',
17
+ /** Material tab label */
18
+ tabLabel: '.mat-mdc-tab',
19
+ /** Material paginator */
20
+ paginator: 'mat-paginator',
21
+ /** Material icon */
22
+ icon: 'mat-icon',
23
+ /** Material autocomplete panel */
24
+ autocompletePanel: '.mat-mdc-autocomplete-panel',
25
+ /** Material autocomplete option */
26
+ autocompleteOption: 'mat-option',
27
+ };
28
+ /**
29
+ * Helper functions for interacting with Angular Material components.
30
+ */
31
+ export class MaterialHelper {
32
+ page;
33
+ constructor(page) {
34
+ this.page = page;
35
+ }
36
+ /** Get all visible table rows, optionally scoped to a parent locator */
37
+ tableRows(scope) {
38
+ const root = scope ?? this.page;
39
+ return root.locator(MaterialSelectors.row);
40
+ }
41
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
42
+ tableRow(index, scope) {
43
+ return this.tableRows(scope).nth(index);
44
+ }
45
+ /** Click on a table row by index (0-based), optionally scoped to a parent locator */
46
+ async clickRow(index, scope) {
47
+ await this.tableRow(index, scope).click();
48
+ }
49
+ /** Get a tab by its label text */
50
+ tabByLabel(text) {
51
+ return this.page.locator(MaterialSelectors.tabLabel, { hasText: text });
52
+ }
53
+ /** Click a tab by its label text */
54
+ async clickTab(text) {
55
+ await this.tabByLabel(text).click();
56
+ }
57
+ /** Fill a mat-form-field input identified by its formControlName.
58
+ * Supports both native inputs with formcontrolname and custom components (e.g. mat-autocomplete-field) wrapping an input. */
59
+ async fillFormControl(formControlName, value) {
60
+ // Try native input first, then fall back to custom component wrapper
61
+ const nativeInput = this.page.locator(`${MaterialSelectors.formField} input[formcontrolname="${formControlName}"]`);
62
+ const wrappedInput = this.page.locator(`[formcontrolname="${formControlName}"] input`);
63
+ const input = (await nativeInput.count()) > 0 ? nativeInput : wrappedInput;
64
+ // Click the parent mat-form-field to ensure focus reaches the input
65
+ const formField = input.locator('xpath=ancestor::mat-form-field');
66
+ if ((await formField.count()) > 0) {
67
+ await formField.first().click();
68
+ }
69
+ else {
70
+ await input.click({ force: true });
71
+ }
72
+ await input.pressSequentially(value, { delay: 50 });
73
+ }
74
+ /**
75
+ * Select an option from a mat-select dropdown by formControlName.
76
+ * Opens the dropdown, waits for options to render, clicks the matching option, then waits for the dropdown to close.
77
+ */
78
+ async selectMatSelectOption(formControlName, optionText) {
79
+ const select = this.page.locator(`mat-select[formcontrolname="${formControlName}"]`);
80
+ await select.click();
81
+ // Use role-based selector: mat-select renders options with role="option" in a listbox
82
+ const option = this.page.getByRole('option', { name: optionText });
83
+ await option.waitFor({ state: 'visible', timeout: 10_000 });
84
+ await option.click();
85
+ // Wait for the option to disappear (dropdown closed)
86
+ await option.waitFor({ state: 'hidden', timeout: 5_000 });
87
+ }
88
+ /** Select an autocomplete option by text after typing in a form control */
89
+ async selectAutocompleteOption(formControlName, searchText, optionText) {
90
+ await this.fillFormControl(formControlName, searchText);
91
+ const panel = this.page.locator(MaterialSelectors.autocompletePanel);
92
+ await panel.waitFor({ state: 'visible', timeout: 10_000 });
93
+ await panel.locator(MaterialSelectors.autocompleteOption, { hasText: optionText }).click();
94
+ }
95
+ /** Wait for table rows to be loaded (at least one row visible), optionally scoped to a parent locator */
96
+ async waitForTableRows(timeout = 15_000, scope) {
97
+ await this.tableRows(scope).first().waitFor({ state: 'visible', timeout });
98
+ }
99
+ }
100
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0ZXJpYWwudXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90ZXN0aW5nL3V0aWxzL21hdGVyaWFsLnV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUc7SUFDL0IseUJBQXlCO0lBQ3pCLEdBQUcsRUFBRSxzQkFBc0I7SUFDM0IsMEJBQTBCO0lBQzFCLFNBQVMsRUFBRSxvQ0FBb0M7SUFDL0Msb0JBQW9CO0lBQ3BCLElBQUksRUFBRSx3QkFBd0I7SUFDOUIsMEJBQTBCO0lBQzFCLFNBQVMsRUFBRSxnQkFBZ0I7SUFDM0IscUJBQXFCO0lBQ3JCLEtBQUssRUFBRSxpQkFBaUI7SUFDeEIseUJBQXlCO0lBQ3pCLFFBQVEsRUFBRSxlQUFlO0lBQ3pCLHlCQUF5QjtJQUN6QixRQUFRLEVBQUUsY0FBYztJQUN4Qix5QkFBeUI7SUFDekIsU0FBUyxFQUFFLGVBQWU7SUFDMUIsb0JBQW9CO0lBQ3BCLElBQUksRUFBRSxVQUFVO0lBQ2hCLGtDQUFrQztJQUNsQyxpQkFBaUIsRUFBRSw2QkFBNkI7SUFDaEQsbUNBQW1DO0lBQ25DLGtCQUFrQixFQUFFLFlBQVk7Q0FDakMsQ0FBQztBQUVGOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGNBQWM7SUFDTDtJQUFwQixZQUFvQixJQUFVO1FBQVYsU0FBSSxHQUFKLElBQUksQ0FBTTtJQUFHLENBQUM7SUFFbEMsd0VBQXdFO0lBQ3hFLFNBQVMsQ0FBQyxLQUFlO1FBQ3ZCLE1BQU0sSUFBSSxHQUFHLEtBQUssSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQ2hDLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUM3QyxDQUFDO0lBRUQsdUZBQXVGO0lBQ3ZGLFFBQVEsQ0FBQyxLQUFhLEVBQUUsS0FBZTtRQUNyQyxPQUFPLElBQUksQ0FBQyxTQUFTLENBQUMsS0FB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LENBQUM7UUFDeEQsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUNyRSxNQUFNLEtBQUssQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBQzNELE1BQU0sS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxrQkFBa0IsRUFBRSxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzdGLENBQUM7SUFFRCx5R0FBeUc7SUFDekcsS0FBSyxDQUFDLGdCQUFnQixDQUFDLE9BQU8sR0FBRyxNQUFNLEVBQUUsS0FBZTtRQUN0RCxNQUFNLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsT0FBTyxDQUFDLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0lBQzdFLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdQTCB2MyBMaWNlbnNlXG5pbXBvcnQgeyBMb2NhdG9yLCBQYWdlIH0gZnJvbSAncGxheXdyaWdodCc7XG5cbi8qKlxuICogVXRpbGl0eSBzZWxlY3RvcnMgYW5kIGhlbHBlcnMgZm9yIEFuZ3VsYXIgTWF0ZXJpYWwgY29tcG9uZW50cyBpbiBlMmUgdGVzdHMuXG4gKi9cbmV4cG9ydCBjb25zdCBNYXRlcmlhbFNlbGVjdG9ycyA9IHtcbiAgLyoqIE1hdGVyaWFsIHRhYmxlIHJvdyAqL1xuICByb3c6ICdtYXQtcm93LCB0clttYXQtcm93XScsXG4gIC8qKiBNYXRlcmlhbCBoZWFkZXIgcm93ICovXG4gIGhlYWRlclJvdzogJ21hdC1oZWFkZXItcm93LCB0clttYXQtaGVhZGVyLXJvd10nLFxuICAvKiogTWF0ZXJpYWwgY2VsbCAqL1xuICBjZWxsOiAnbWF0LWNlbGwsIHRkW21hdC1jZWxsXScsXG4gIC8qKiBNYXRlcmlhbCBmb3JtIGZpZWxkICovXG4gIGZvcm1GaWVsZDogJ21hdC1mb3JtLWZpZWxkJyxcbiAgLyoqIE1hdGVyaWFsIGlucHV0ICovXG4gIGlucHV0OiAnaW5wdXRbbWF0SW5wdXRdJyxcbiAgLyoqIE1hdGVyaWFsIHRhYiBncm91cCAqL1xuICB0YWJHcm91cDogJ21hdC10YWItZ3JvdXAnLFxuICAvKiogTWF0ZXJpYWwgdGFiIGxhYmVsICovXG4gIHRhYkxhYmVsOiAnLm1hdC1tZGMtdGFiJyxcbiAgLyoqIE1hdGVyaWFsIHBhZ2luYXRvciAqL1xuICBwYWdpbmF0b3I6ICdtYXQtcGFnaW5hdG9yJyxcbiAgLyoqIE1hdGVyaWFsIGljb24gKi9cbiAgaWNvbjogJ21hdC1pY29uJyxcbiAgLyoqIE1hdGVyaWFsIGF1dG9jb21wbGV0ZSBwYW5lbCAqL1xuICBhdXRvY29tcGxldGVQYW5lbDogJy5tYXQtbWRjLWF1dG9jb21wbGV0ZS1wYW5lbCcsXG4gIC8qKiBNYXRlcmlhbCBhdXRvY29tcGxldGUgb3B0aW9uICovXG4gIGF1dG9jb21wbGV0ZU9wdGlvbjogJ21hdC1vcHRpb24nLFxufTtcblxuLyoqXG4gKiBIZWxwZXIgZnVuY3Rpb25zIGZvciBpbnRlcmFjdGluZyB3aXRoIEFuZ3VsYXIgTWF0ZXJpYWwgY29tcG9uZW50cy5cbiAqL1xuZXhwb3J0IGNsYXNzIE1hdGVyaWFsSGVscGVyIHtcbiAgY29uc3RydWN0b3IocHJpdmF0ZSBwYWdlOiBQYWdlKSB7fVxuXG4gIC8qKiBHZXQgYWxsIHZpc2libGUgdGFibGUgcm93cywgb3B0aW9uYWxseSBzY29wZWQgdG8gYSBwYXJlbnQgbG9jYXRvciAqL1xuICB0YWJsZVJvd3Moc2NvcGU/OiBMb2NhdG9yKTogTG9jYXRvciB7XG4gICAgY29uc3Qgcm9vdCA9IHNjb3BlID8/IHRoaXMucGFnZTtcbiAgICByZXR1cm4gcm9vdC5sb2NhdG9yKE1hdGVyaWFsU2VsZWN0b3JzLnJvdyk7XG4gIH1cblxuICAvKiogR2V0IGEgc2luZ2xlIHRhYmxlIHJvdyBieSBpbmRleCAoMC1iYXNlZCksIG9wdGlvbmFsbHkgc2NvcGVkIHRvIGEgcGFyZW50IGxvY2F0b3IgKi9cbiAgdGFibGVSb3coaW5kZXg6IG51bWJlciwgc2NvcGU/OiBMb2NhdG9yKTogTG9jYXRvciB7XG4gICAgcmV0dXJuIHRoaXMudGFibGVSb3dzKHNjb3BlKS5udGgoaW5kZXgpO1xuICB9XG5cbiAgLyoqIENsaWNrIG9uIGEgdGFibGUgcm93IGJ5IGluZGV4ICgwLWJhc2VkKSwgb3B0aW9uYWxseSBzY29wZWQgdG8gYSBwYXJlbnQgbG9jYXRvciAqL1xuICBhc3luYyBjbGlja1JvdyhpbmRleDogbnVtYmVyLCBzY29wZT86IExvY2F0b3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnRhYmxlUm93KGluZGV4LCBzY29wZSkuY2xpY2soKTtcbiAgfVxuXG4gIC8qKiBHZXQgYSB0YWIgYnkgaXRzIGxhYmVsIHRleHQgKi9cbiAgdGFiQnlMYWJlbCh0ZXh0OiBzdHJpbmcgfCBSZWdFeHApOiBMb2NhdG9yIHtcbiAgICByZXR1cm4gdGhpcy5wYWdlLmxvY2F0b3IoTWF0ZXJpYWxTZWxlY3RvcnMudGFiTGFiZWwsIHsgaGFzVGV4dDogdGV4dCB9KTtcbiAgfVxuXG4gIC8qKiBDbGljayBhIHRhYiBieSBpdHMgbGFiZWwgdGV4dCAqL1xuICBhc3luYyBjbGlja1RhYih0ZXh0OiBzdHJpbmcgfCBSZWdFeHApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnRhYkJ5TGFiZWwodGV4dCkuY2xpY2soKTtcbiAgfVxuXG4gIC8qKiBGaWxsIGEgbWF0LWZvcm0tZmllbGQgaW5wdXQgaWRlbnRpZmllZCBieSBpdHMgZm9ybUNvbnRyb2xOYW1lLlxuICAgKiAgU3VwcG9ydHMgYm90aCBuYXRpdmUgaW5wdXRzIHdpdGggZm9ybWNvbnRyb2xuYW1lIGFuZCBjdXN0b20gY29tcG9uZW50cyAoZS5nLiBtYXQtYXV0b2NvbXBsZXRlLWZpZWxkKSB3cmFwcGluZyBhbiBpbnB1dC4gKi9cbiAgYXN5bmMgZmlsbEZvcm1Db250cm9sKGZvcm1Db250cm9sTmFtZTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gVHJ5IG5hdGl2ZSBpbnB1dCBmaXJzdCwgdGhlbiBmYWxsIGJhY2sgdG8gY3VzdG9tIGNvbXBvbmVudCB3cmFwcGVyXG4gICAgY29uc3QgbmF0aXZlSW5wdXQgPSB0aGlzLnBhZ2UubG9jYXRvcihgJHtNYXRlcmlhbFNlbGVjdG9ycy5mb3JtRmllbGR9IGlucHV0W2Zvcm1jb250cm9sbmFtZT1cIiR7Zm9ybUNvbnRyb2xOYW1lfVwiXWApO1xuICAgIGNvbnN0IHdyYXBwZWRJbnB1dCA9IHRoaXMucGFnZS5sb2NhdG9yKGBbZm9ybWNvbnRyb2xuYW1lPVwiJHtmb3JtQ29udHJvbE5hbWV9XCJdIGlucHV0YCk7XG4gICAgY29uc3QgaW5wdXQgPSAoYXdhaXQgbmF0aXZlSW5wdXQuY291bnQoKSkgPiAwID8gbmF0aXZlSW5wdXQgOiB3cmFwcGVkSW5wdXQ7XG4gICAgLy8gQ2xpY2sgdGhlIHBhcmVudCBtYXQtZm9ybS1maWVsZCB0byBlbnN1cmUgZm9jdXMgcmVhY2hlcyB0aGUgaW5wdXRcbiAgICBjb25zdCBmb3JtRmllbGQgPSBpbnB1dC5sb2NhdG9yKCd4cGF0aD1hbmNlc3Rvcjo6bWF0LWZvcm0tZmllbGQnKTtcbiAgICBpZiAoKGF3YWl0IGZvcm1GaWVsZC5jb3VudCgpKSA+IDApIHtcbiAgICAgIGF3YWl0IGZvcm1GaWVsZC5maXJzdCgpLmNsaWNrKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IGlucHV0LmNsaWNrKHsgZm9yY2U6IHRydWUgfSk7XG4gICAgfVxuICAgIGF3YWl0IGlucHV0LnByZXNzU2VxdWVudGlhbGx5KHZhbHVlLCB7IGRlbGF5OiA1MCB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZWxlY3QgYW4gb3B0aW9uIGZyb20gYSBtYXQtc2VsZWN0IGRyb3Bkb3duIGJ5IGZvcm1Db250cm9sTmFtZS5cbiAgICogT3BlbnMgdGhlIGRyb3Bkb3duLCB3YWl0cyBmb3Igb3B0aW9ucyB0byByZW5kZXIsIGNsaWNrcyB0aGUgbWF0Y2hpbmcgb3B0aW9uLCB0aGVuIHdhaXRzIGZvciB0aGUgZHJvcGRvd24gdG8gY2xvc2UuXG4gICAqL1xuICBhc3luYyBzZWxlY3RNYXRTZWxlY3RPcHRpb24oZm9ybUNvbnRyb2xOYW1lOiBzdHJpbmcsIG9wdGlvblRleHQ6IHN0cmluZyB8IFJlZ0V4cCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHNlbGVjdCA9IHRoaXMucGFnZS5sb2NhdG9yKGBtYXQtc2VsZWN0W2Zvcm1jb250cm9sbmFtZT1cIiR7Zm9ybUNvbnRyb2xOYW1lfVwiXWApO1xuICAgIGF3YWl0IHNlbGVjdC5jbGljaygpO1xuICAgIC8vIFVzZSByb2xlLWJhc2VkIHNlbGVjdG9yOiBtYXQtc2VsZWN0IHJlbmRlcnMgb3B0aW9ucyB3aXRoIHJvbGU9XCJvcHRpb25cIiBpbiBhIGxpc3Rib3hcbiAgICBjb25zdCBvcHRpb24gPSB0aGlzLnBhZ2UuZ2V0QnlSb2xlKCdvcHRpb24nLCB7IG5hbWU6IG9wdGlvblRleHQgfSk7XG4gICAgYXdhaXQgb3B0aW9uLndhaXRGb3IoeyBzdGF0ZTogJ3Zpc2libGUnLCB0aW1lb3V0OiAxMF8wMDAgfSk7XG4gICAgYXdhaXQgb3B0aW9uLmNsaWNrKCk7XG4gICAgLy8gV2FpdCBmb3IgdGhlIG9wdGlvbiB0byBkaXNhcHBlYXIgKGRyb3Bkb3duIGNsb3NlZClcbiAgICBhd2FpdCBvcHRpb24ud2FpdEZvcih7IHN0YXRlOiAnaGlkZGVuJywgdGltZW91dDogNV8wMDAgfSk7XG4gIH1cblxuICAvKiogU2VsZWN0IGFuIGF1dG9jb21wbGV0ZSBvcHRpb24gYnkgdGV4dCBhZnRlciB0eXBpbmcgaW4gYSBmb3JtIGNvbnRyb2wgKi9cbiAgYXN5bmMgc2VsZWN0QXV0b2NvbXBsZXRlT3B0aW9uKGZvcm1Db250cm9sTmFtZTogc3RyaW5nLCBzZWFyY2hUZXh0OiBzdHJpbmcsIG9wdGlvblRleHQ6IHN0cmluZyB8IFJlZ0V4cCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuZmlsbEZvcm1Db250cm9sKGZvcm1Db250cm9sTmFtZSwgc2VhcmNoVGV4dCk7XG4gICAgY29uc3QgcGFuZWwgPSB0aGlzLnBhZ2UubG9jYXRvcihNYXRlcmlhbFNlbGVjdG9ycy5hdXRvY29tcGxldGVQYW5lbCk7XG4gICAgYXdhaXQgcGFuZWwud2FpdEZvcih7IHN0YXRlOiAndmlzaWJsZScsIHRpbWVvdXQ6IDEwXzAwMCB9KTtcbiAgICBhd2FpdCBwYW5lbC5sb2NhdG9yKE1hdGVyaWFsU2VsZWN0b3JzLmF1dG9jb21wbGV0ZU9wdGlvbiwgeyBoYXNUZXh0OiBvcHRpb25UZXh0IH0pLmNsaWNrKCk7XG4gIH1cblxuICAvKiogV2FpdCBmb3IgdGFibGUgcm93cyB0byBiZSBsb2FkZWQgKGF0IGxlYXN0IG9uZSByb3cgdmlzaWJsZSksIG9wdGlvbmFsbHkgc2NvcGVkIHRvIGEgcGFyZW50IGxvY2F0b3IgKi9cbiAgYXN5bmMgd2FpdEZvclRhYmxlUm93cyh0aW1lb3V0ID0gMTVfMDAwLCBzY29wZT86IExvY2F0b3IpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCB0aGlzLnRhYmxlUm93cyhzY29wZSkuZmlyc3QoKS53YWl0Rm9yKHsgc3RhdGU6ICd2aXNpYmxlJywgdGltZW91dCB9KTtcbiAgfVxufSJdfQ==
@@ -0,0 +1,4 @@
1
+ // Standalone startup constants for the testing package.
2
+ // Must be kept in sync with src/app/core/services/startup.constants.ts
3
+ export const STARTUP_DATA_STORAGE_KEY = '__appStartupData';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnR1cC5jb25zdGFudHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90ZXN0aW5nL3V0aWxzL3N0YXJ0dXAuY29uc3RhbnRzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHdEQUF3RDtBQUN4RCx1RUFBdUU7QUFDdkUsTUFBTSxDQUFDLE1BQU0sd0JBQXdCLEdBQUcsa0JBQWtCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTdGFuZGFsb25lIHN0YXJ0dXAgY29uc3RhbnRzIGZvciB0aGUgdGVzdGluZyBwYWNrYWdlLlxuLy8gTXVzdCBiZSBrZXB0IGluIHN5bmMgd2l0aCBzcmMvYXBwL2NvcmUvc2VydmljZXMvc3RhcnR1cC5jb25zdGFudHMudHNcbmV4cG9ydCBjb25zdCBTVEFSVFVQX0RBVEFfU1RPUkFHRV9LRVkgPSAnX19hcHBTdGFydHVwRGF0YSc7XG4iXX0=
@@ -0,0 +1,139 @@
1
+ import { IonicHelper, IonicSelectors } from './ionic.utils';
2
+ import { MaterialHelper, MaterialSelectors } from './material.utils';
3
+ /**
4
+ * Common UI helper functions to improve readability of e2e tests.
5
+ * This helper centralizes access to Ionic and Material specific helpers.
6
+ */
7
+ export class UiHelper {
8
+ page;
9
+ ionic;
10
+ mat;
11
+ constructor(page) {
12
+ this.page = page;
13
+ this.ionic = new IonicHelper(page);
14
+ this.mat = new MaterialHelper(page);
15
+ }
16
+ /**
17
+ * Click an element and wait for it to be ready.
18
+ * @param locator The locator to click.
19
+ * @param timeout The maximum time to wait.
20
+ */
21
+ async click(locator, timeout = 10_000) {
22
+ await locator.waitFor({ state: 'visible', timeout });
23
+ // force: true bypasses Ionic overlay elements (ion-col, ion-toolbar) that intercept pointer events
24
+ await locator.click({ force: true });
25
+ }
26
+ /**
27
+ * Fill an input and wait for it to be ready.
28
+ * @param locator The locator to fill.
29
+ * @param value The value to enter.
30
+ * @param timeout The maximum time to wait.
31
+ */
32
+ async fill(locator, value, timeout = 10_000) {
33
+ await locator.waitFor({ state: 'visible', timeout });
34
+ await locator.fill(value);
35
+ }
36
+ /**
37
+ * Wait for an element to be visible.
38
+ * @param locator The locator to wait for.
39
+ * @param timeout The maximum time to wait.
40
+ */
41
+ async waitForVisible(locator, timeout = 10_000) {
42
+ await locator.waitFor({ state: 'visible', timeout });
43
+ }
44
+ /**
45
+ * Wait for an element to be hidden.
46
+ * @param locator The locator to wait for.
47
+ * @param timeout The maximum time to wait.
48
+ */
49
+ async waitForHidden(locator, timeout = 15_000) {
50
+ await locator.waitFor({ state: 'hidden', timeout });
51
+ }
52
+ /* --- Unified Selectors --- */
53
+ /**
54
+ * Get a button by its text content.
55
+ * Supports both Ionic and Material buttons.
56
+ * @param text The text content to match.
57
+ * @param scope Optional locator to scope the search within.
58
+ */
59
+ button(text, scope) {
60
+ // Combine selectors for both Ionic and Material buttons
61
+ const selectors = [
62
+ IonicSelectors.button,
63
+ 'button[mat-button]',
64
+ 'button[mat-raised-button]',
65
+ 'button[mat-flat-button]',
66
+ 'button[mat-stroked-button]',
67
+ 'button[mat-icon-button]',
68
+ 'button[mat-fab]',
69
+ 'button[mat-mini-fab]',
70
+ ].join(', ');
71
+ const root = scope ?? this.page;
72
+ return root.locator(selectors, { hasText: text });
73
+ }
74
+ /**
75
+ * Get an icon button by its icon name.
76
+ * Supports both Ionic and Material icon buttons.
77
+ * @param iconName The icon name (e.g. 'mail', 'search', 'ellipsis-horizontal').
78
+ * @param scope Optional locator to scope the search within.
79
+ */
80
+ iconButton(iconName, scope) {
81
+ const matIconSelector = `${MaterialSelectors.icon}:has-text("${iconName}")`;
82
+ const ionIconSelector = `ion-icon[name="${iconName}"]`;
83
+ const root = scope ?? this.page;
84
+ return root.locator(`button, ion-button`).filter({
85
+ has: this.page.locator(`${matIconSelector}, ${ionIconSelector}`),
86
+ });
87
+ }
88
+ /**
89
+ * Access to the underlying Playwright Page object.
90
+ */
91
+ get pageObj() {
92
+ return this.page;
93
+ }
94
+ /** Get a modal by its selector */
95
+ modal(selector) {
96
+ return this.ionic.modal(selector);
97
+ }
98
+ /** Get a tab by its label text */
99
+ tab(text) {
100
+ return this.mat.tabByLabel(text);
101
+ }
102
+ /* --- Specialized Helpers (delegated) --- */
103
+ /** Wait for the app to be ready */
104
+ async waitForAppReady(timeout = 15_000) {
105
+ await this.ionic.waitForAppReady(timeout);
106
+ }
107
+ /** Wait for loading spinners to disappear */
108
+ async waitForLoadingComplete(timeout = 15_000) {
109
+ await this.ionic.waitForLoadingComplete(timeout);
110
+ }
111
+ /** Wait for table rows to be loaded, optionally scoped to a parent locator */
112
+ async waitForTableRows(timeout = 15_000, scope) {
113
+ await this.mat.waitForTableRows(timeout, scope);
114
+ }
115
+ /** Get a single table row by index (0-based), optionally scoped to a parent locator */
116
+ tableRow(index, scope) {
117
+ return this.mat.tableRow(index, scope);
118
+ }
119
+ /** Click on a table row by index, optionally scoped to a parent locator */
120
+ async clickRow(index, scope) {
121
+ await this.click(this.tableRow(index, scope));
122
+ }
123
+ /** Fill a mat-form-field input identified by its formControlName */
124
+ async fillFormControl(formControlName, value) {
125
+ await this.mat.fillFormControl(formControlName, value);
126
+ }
127
+ /** Select an autocomplete option */
128
+ async selectAutocompleteOption(formControlName, searchText, optionText) {
129
+ await this.mat.selectAutocompleteOption(formControlName, searchText, optionText);
130
+ }
131
+ /**
132
+ * Select an option from a mat-select dropdown.
133
+ * Opens the dropdown, waits for options, clicks the matching one, then waits for the dropdown to close.
134
+ */
135
+ async selectOption(formControlName, optionText) {
136
+ await this.mat.selectMatSelectOption(formControlName, optionText);
137
+ }
138
+ }
139
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidWkudXRpbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90ZXN0aW5nL3V0aWxzL3VpLnV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUVyRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sUUFBUTtJQUlHO0lBSFosS0FBSyxDQUFjO0lBQ25CLEdBQUcsQ0FBaUI7SUFFOUIsWUFBc0IsSUFBVTtRQUFWLFNBQUksR0FBSixJQUFJLENBQU07UUFDOUIsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLFdBQVcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNuQyxJQUFJLENBQUMsR0FBRyxHQUFHLElBQUksY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFnQixFQUFFLE9BQU8sR0FBRyxNQUFNO1FBQzVDLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztRQUNyRCxtR0FBbUc7UUFDbkcsTUFBTSxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDdkMsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0gsS0FBSyxDQUFDLElBQUksQ0FBQyxPQUFnQixFQUFFLEtBQWEsRUFBRSxPQUFPLEdBQUcsTUFBTTtRQUMxRCxNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFDckQsTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLENB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QUssRUFBRSxLQUFLLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQsMkVBQTJFO0lBQzNFLEtBQUssQ0FBQyxRQUFRLENBQUMsS0FBYSxFQUFFLEtBQWU7UUFDM0MsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUM7SUFDaEQsQ0FBQztJQUVELG9FQUFvRTtJQUNwRSxLQUFLLENBQUMsZUFBZSxDQUFDLGVBQXVCLEVBQUUsS0FBYTtRQUMxRCxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLGVBQWUsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUN6RCxDQUFDO0lBRUQsb0NBQW9DO0lBQ3BDLEtBQUssQ0FBQyx3QkFBd0IsQ0FBQyxlQUF1QixFQUFFLFVBQWtCLEVBQUUsVUFBMkI7UUFDckcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLGVBQWUsRUFBRSxVQUFVLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDbkYsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxZQUFZLENBQUMsZUFBdUIsRUFBRSxVQUEyQjtRQUNyRSxNQUFNLElBQUksQ0FBQyxHQUFHLENBQUMscUJBQXFCLENBQUMsZUFBZSxFQUFFLFVBQVUsQ0FBQyxDQUFDO0lBQ3BFLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdQTCB2MyBMaWNlbnNlXG5pbXBvcnQgeyBMb2NhdG9yLCBQYWdlIH0gZnJvbSAncGxheXdyaWdodCc7XG5pbXBvcnQgeyBJb25pY0hlbHBlciwgSW9uaWNTZWxlY3RvcnMgfSBmcm9tICcuL2lvbmljLnV0aWxzJztcbmltcG9ydCB7IE1hdGVyaWFsSGVscGVyLCBNYXRlcmlhbFNlbGVjdG9ycyB9IGZyb20gJy4vbWF0ZXJpYWwudXRpbHMnO1xuXG4vKipcbiAqIENvbW1vbiBVSSBoZWxwZXIgZnVuY3Rpb25zIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkgb2YgZTJlIHRlc3RzLlxuICogVGhpcyBoZWxwZXIgY2VudHJhbGl6ZXMgYWNjZXNzIHRvIElvbmljIGFuZCBNYXRlcmlhbCBzcGVjaWZpYyBoZWxwZXJzLlxuICovXG5leHBvcnQgY2xhc3MgVWlIZWxwZXIge1xuICBwcm90ZWN0ZWQgaW9uaWM6IElvbmljSGVscGVyO1xuICBwcm90ZWN0ZWQgbWF0OiBNYXRlcmlhbEhlbHBlcjtcblxuICBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgcGFnZTogUGFnZSkge1xuICAgIHRoaXMuaW9uaWMgPSBuZXcgSW9uaWNIZWxwZXIocGFnZSk7XG4gICAgdGhpcy5tYXQgPSBuZXcgTWF0ZXJpYWxIZWxwZXIocGFnZSk7XG4gIH1cblxuICAvKipcbiAgICogQ2xpY2sgYW4gZWxlbWVudCBhbmQgd2FpdCBmb3IgaXQgdG8gYmUgcmVhZHkuXG4gICAqIEBwYXJhbSBsb2NhdG9yIFRoZSBsb2NhdG9yIHRvIGNsaWNrLlxuICAgKiBAcGFyYW0gdGltZW91dCBUaGUgbWF4aW11bSB0aW1lIHRvIHdhaXQuXG4gICAqL1xuICBhc3luYyBjbGljayhsb2NhdG9yOiBMb2NhdG9yLCB0aW1lb3V0ID0gMTBfMDAwKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgbG9jYXRvci53YWl0Rm9yKHsgc3RhdGU6ICd2aXNpYmxlJywgdGltZW91dCB9KTtcbiAgICAvLyBmb3JjZTogdHJ1ZSBieXBhc3NlcyBJb25pYyBvdmVybGF5IGVsZW1lbnRzIChpb24tY29sLCBpb24tdG9vbGJhcikgdGhhdCBpbnRlcmNlcHQgcG9pbnRlciBldmVudHNcbiAgICBhd2FpdCBsb2NhdG9yLmNsaWNrKHsgZm9yY2U6IHRydWUgfSk7XG4gIH1cblxuICAvKipcbiAgICogRmlsbCBhbiBpbnB1dCBhbmQgd2FpdCBmb3IgaXQgdG8gYmUgcmVhZHkuXG4gICAqIEBwYXJhbSBsb2NhdG9yIFRoZSBsb2NhdG9yIHRvIGZpbGwuXG4gICAqIEBwYXJhbSB2YWx1ZSBUaGUgdmFsdWUgdG8gZW50ZXIuXG4gICAqIEBwYXJhbSB0aW1lb3V0IFRoZSBtYXhpbXVtIHRpbWUgdG8gd2FpdC5cbiAgICovXG4gIGFzeW5jIGZpbGwobG9jYXRvcjogTG9jYXRvciwgdmFsdWU6IHN0cmluZywgdGltZW91dCA9IDEwXzAwMCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IGxvY2F0b3Iud2FpdEZvcih7IHN0YXRlOiAndmlzaWJsZScsIHRpbWVvdXQgfSk7XG4gICAgYXdhaXQgbG9jYXRvci5maWxsKHZhbHVlKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0IGZvciBhbiBlbGVtZW50IHRvIGJlIHZpc2libGUuXG4gICAqIEBwYXJhbSBsb2NhdG9yIFRoZSBsb2NhdG9yIHRvIHdhaXQgZm9yLlxuICAgKiBAcGFyYW0gdGltZW91dCBUaGUgbWF4aW11bSB0aW1lIHRvIHdhaXQuXG4gICAqL1xuICBhc3luYyB3YWl0Rm9yVmlzaWJsZShsb2NhdG9yOiBMb2NhdG9yLCB0aW1lb3V0ID0gMTBfMDAwKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgbG9jYXRvci53YWl0Rm9yKHsgc3RhdGU6ICd2aXNpYmxlJywgdGltZW91dCB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0IGZvciBhbiBlbGVtZW50IHRvIGJlIGhpZGRlbi5cbiAgICogQHBhcmFtIGxvY2F0b3IgVGhlIGxvY2F0b3IgdG8gd2FpdCBmb3IuXG4gICAqIEBwYXJhbSB0aW1lb3V0IFRoZSBtYXhpbXVtIHRpbWUgdG8gd2FpdC5cbiAgICovXG4gIGFzeW5jIHdhaXRGb3JIaWRkZW4obG9jYXRvcjogTG9jYXRvciwgdGltZW91dCA9IDE1XzAwMCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IGxvY2F0b3Iud2FpdEZvcih7IHN0YXRlOiAnaGlkZGVuJywgdGltZW91dCB9KTtcbiAgfVxuXG4gIC8qIC0tLSBVbmlmaWVkIFNlbGVjdG9ycyAtLS0gKi9cblxuICAvKipcbiAgICogR2V0IGEgYnV0dG9uIGJ5IGl0cyB0ZXh0IGNvbnRlbnQuXG4gICAqIFN1cHBvcnRzIGJvdGggSW9uaWMgYW5kIE1hdGVyaWFsIGJ1dHRvbnMuXG4gICAqIEBwYXJhbSB0ZXh0IFRoZSB0ZXh0IGNvbnRlbnQgdG8gbWF0Y2guXG4gICAqIEBwYXJhbSBzY29wZSBPcHRpb25hbCBsb2NhdG9yIHRvIHNjb3BlIHRoZSBzZWFyY2ggd2l0aGluLlxuICAgKi9cbiAgYnV0dG9uKHRleHQ6IHN0cmluZyB8IFJlZ0V4cCwgc2NvcGU/OiBMb2NhdG9yKTogTG9jYXRvciB7XG4gICAgLy8gQ29tYmluZSBzZWxlY3RvcnMgZm9yIGJvdGggSW9uaWMgYW5kIE1hdGVyaWFsIGJ1dHRvbnNcbiAgICBjb25zdCBzZWxlY3RvcnMgPSBbXG4gICAgICBJb25pY1NlbGVjdG9ycy5idXR0b24sXG4gICAgICAnYnV0dG9uW21hdC1idXR0b25dJyxcbiAgICAgICdidXR0b25bbWF0LXJhaXNlZC1idXR0b25dJyxcbiAgICAgICdidXR0b25bbWF0LWZsYXQtYnV0dG9uXScsXG4gICAgICAnYnV0dG9uW21hdC1zdHJva2VkLWJ1dHRvbl0nLFxuICAgICAgJ2J1dHRvblttYXQtaWNvbi1idXR0b25dJyxcbiAgICAgICdidXR0b25bbWF0LWZhYl0nLFxuICAgICAgJ2J1dHRvblttYXQtbWluaS1mYWJdJyxcbiAgICBdLmpvaW4oJywgJyk7XG5cbiAgICBjb25zdCByb290ID0gc2NvcGUgPz8gdGhpcy5wYWdlO1xuICAgIHJldHVybiByb290LmxvY2F0b3Ioc2VsZWN0b3JzLCB7IGhhc1RleHQ6IHRleHQgfSk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFuIGljb24gYnV0dG9uIGJ5IGl0cyBpY29uIG5hbWUuXG4gICAqIFN1cHBvcnRzIGJvdGggSW9uaWMgYW5kIE1hdGVyaWFsIGljb24gYnV0dG9ucy5cbiAgICogQHBhcmFtIGljb25OYW1lIFRoZSBpY29uIG5hbWUgKGUuZy4gJ21haWwnLCAnc2VhcmNoJywgJ2VsbGlwc2lzLWhvcml6b250YWwnKS5cbiAgICogQHBhcmFtIHNjb3BlIE9wdGlvbmFsIGxvY2F0b3IgdG8gc2NvcGUgdGhlIHNlYXJjaCB3aXRoaW4uXG4gICAqL1xuICBpY29uQnV0dG9uKGljb25OYW1lOiBzdHJpbmcsIHNjb3BlPzogTG9jYXRvcik6IExvY2F0b3Ige1xuICAgIGNvbnN0IG1hdEljb25TZWxlY3RvciA9IGAke01hdGVyaWFsU2VsZWN0b3JzLmljb259Omhhcy10ZXh0KFwiJHtpY29uTmFtZX1cIilgO1xuICAgIGNvbnN0IGlvbkljb25TZWxlY3RvciA9IGBpb24taWNvbltuYW1lPVwiJHtpY29uTmFtZX1cIl1gO1xuICAgIGNvbnN0IHJvb3QgPSBzY29wZSA/PyB0aGlzLnBhZ2U7XG4gICAgcmV0dXJuIHJvb3QubG9jYXRvcihgYnV0dG9uLCBpb24tYnV0dG9uYCkuZmlsdGVyKHtcbiAgICAgIGhhczogdGhpcy5wYWdlLmxvY2F0b3IoYCR7bWF0SWNvblNlbGVjdG9yfSwgJHtpb25JY29uU2VsZWN0b3J9YCksXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQWNjZXNzIHRvIHRoZSB1bmRlcmx5aW5nIFBsYXl3cmlnaHQgUGFnZSBvYmplY3QuXG4gICAqL1xuICBnZXQgcGFnZU9iaigpOiBQYWdlIHtcbiAgICByZXR1cm4gdGhpcy5wYWdlO1xuICB9XG5cbiAgLyoqIEdldCBhIG1vZGFsIGJ5IGl0cyBzZWxlY3RvciAqL1xuICBtb2RhbChzZWxlY3Rvcjogc3RyaW5nKTogTG9jYXRvciB7XG4gICAgcmV0dXJuIHRoaXMuaW9uaWMubW9kYWwoc2VsZWN0b3IpO1xuICB9XG5cbiAgLyoqIEdldCBhIHRhYiBieSBpdHMgbGFiZWwgdGV4dCAqL1xuICB0YWIodGV4dDogc3RyaW5nIHwgUmVnRXhwKTogTG9jYXRvciB7XG4gICAgcmV0dXJuIHRoaXMubWF0LnRhYkJ5TGFiZWwodGV4dCk7XG4gIH1cblxuICAvKiAtLS0gU3BlY2lhbGl6ZWQgSGVscGVycyAoZGVsZWdhdGVkKSAtLS0gKi9cblxuICAvKiogV2FpdCBmb3IgdGhlIGFwcCB0byBiZSByZWFkeSAqL1xuICBhc3luYyB3YWl0Rm9yQXBwUmVhZHkodGltZW91dCA9IDE1XzAwMCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuaW9uaWMud2FpdEZvckFwcFJlYWR5KHRpbWVvdXQpO1xuICB9XG5cbiAgLyoqIFdhaXQgZm9yIGxvYWRpbmcgc3Bpbm5lcnMgdG8gZGlzYXBwZWFyICovXG4gIGFzeW5jIHdhaXRGb3JMb2FkaW5nQ29tcGxldGUodGltZW91dCA9IDE1XzAwMCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuaW9uaWMud2FpdEZvckxvYWRpbmdDb21wbGV0ZSh0aW1lb3V0KTtcbiAgfVxuXG4gIC8qKiBXYWl0IGZvciB0YWJsZSByb3dzIHRvIGJlIGxvYWRlZCwgb3B0aW9uYWxseSBzY29wZWQgdG8gYSBwYXJlbnQgbG9jYXRvciAqL1xuICBhc3luYyB3YWl0Rm9yVGFibGVSb3dzKHRpbWVvdXQgPSAxNV8wMDAsIHNjb3BlPzogTG9jYXRvcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMubWF0LndhaXRGb3JUYWJsZVJvd3ModGltZW91dCwgc2NvcGUpO1xuICB9XG5cbiAgLyoqIEdldCBhIHNpbmdsZSB0YWJsZSByb3cgYnkgaW5kZXggKDAtYmFzZWQpLCBvcHRpb25hbGx5IHNjb3BlZCB0byBhIHBhcmVudCBsb2NhdG9yICovXG4gIHRhYmxlUm93KGluZGV4OiBudW1iZXIsIHNjb3BlPzogTG9jYXRvcik6IExvY2F0b3Ige1xuICAgIHJldHVybiB0aGlzLm1hdC50YWJsZVJvdyhpbmRleCwgc2NvcGUpO1xuICB9XG5cbiAgLyoqIENsaWNrIG9uIGEgdGFibGUgcm93IGJ5IGluZGV4LCBvcHRpb25hbGx5IHNjb3BlZCB0byBhIHBhcmVudCBsb2NhdG9yICovXG4gIGFzeW5jIGNsaWNrUm93KGluZGV4OiBudW1iZXIsIHNjb3BlPzogTG9jYXRvcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuY2xpY2sodGhpcy50YWJsZVJvdyhpbmRleCwgc2NvcGUpKTtcbiAgfVxuXG4gIC8qKiBGaWxsIGEgbWF0LWZvcm0tZmllbGQgaW5wdXQgaWRlbnRpZmllZCBieSBpdHMgZm9ybUNvbnRyb2xOYW1lICovXG4gIGFzeW5jIGZpbGxGb3JtQ29udHJvbChmb3JtQ29udHJvbE5hbWU6IHN0cmluZywgdmFsdWU6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMubWF0LmZpbGxGb3JtQ29udHJvbChmb3JtQ29udHJvbE5hbWUsIHZhbHVlKTtcbiAgfVxuXG4gIC8qKiBTZWxlY3QgYW4gYXV0b2NvbXBsZXRlIG9wdGlvbiAqL1xuICBhc3luYyBzZWxlY3RBdXRvY29tcGxldGVPcHRpb24oZm9ybUNvbnRyb2xOYW1lOiBzdHJpbmcsIHNlYXJjaFRleHQ6IHN0cmluZywgb3B0aW9uVGV4dDogc3RyaW5nIHwgUmVnRXhwKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5tYXQuc2VsZWN0QXV0b2NvbXBsZXRlT3B0aW9uKGZvcm1Db250cm9sTmFtZSwgc2VhcmNoVGV4dCwgb3B0aW9uVGV4dCk7XG4gIH1cblxuICAvKipcbiAgICogU2VsZWN0IGFuIG9wdGlvbiBmcm9tIGEgbWF0LXNlbGVjdCBkcm9wZG93bi5cbiAgICogT3BlbnMgdGhlIGRyb3Bkb3duLCB3YWl0cyBmb3Igb3B0aW9ucywgY2xpY2tzIHRoZSBtYXRjaGluZyBvbmUsIHRoZW4gd2FpdHMgZm9yIHRoZSBkcm9wZG93biB0byBjbG9zZS5cbiAgICovXG4gIGFzeW5jIHNlbGVjdE9wdGlvbihmb3JtQ29udHJvbE5hbWU6IHN0cmluZywgb3B0aW9uVGV4dDogc3RyaW5nIHwgUmVnRXhwKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgdGhpcy5tYXQuc2VsZWN0TWF0U2VsZWN0T3B0aW9uKGZvcm1Db250cm9sTmFtZSwgb3B0aW9uVGV4dCk7XG4gIH1cbn0iXX0=