diff --git a/.github/workflows/dotnet-core-master.yml b/.github/workflows/dotnet-core-master.yml index 6f8f1089b..41e62159c 100644 --- a/.github/workflows/dotnet-core-master.yml +++ b/.github/workflows/dotnet-core-master.yml @@ -76,23 +76,12 @@ jobs: cp -av eform-angular-timeplanning-plugin/eform-client/playwright/helpers eform-angular-frontend/eform-client/playwright/helpers cp -av eform-angular-timeplanning-plugin/eform-client/playwright.config.ts eform-angular-frontend/eform-client/playwright.config.ts cd eform-angular-frontend/eform-client && ../../eform-angular-timeplanning-plugin/testinginstallpn.sh + - name: Enable Corepack + run: corepack enable - name: yarn install run: cd eform-angular-frontend/eform-client && yarn install - name: Run Angular unit tests - run: | - cd eform-angular-frontend/eform-client - # Check if Jest is configured - if [ ! -f "jest.config.js" ] && [ ! -f "jest.config.ts" ]; then - echo "⚠️ Jest is not configured in the frontend repository." - echo "Unit tests require Jest to be configured. Skipping unit tests." - echo "" - echo "To enable unit tests, ensure Jest is configured in the frontend repository." - exit 0 - fi - - # Run Jest tests for time-planning-pn plugin - echo "Running Jest tests for time-planning-pn plugin..." - npm run test:unit -- --testPathPatterns=time-planning-pn --coverage --collectCoverageFrom='src/app/plugins/modules/time-planning-pn/**/*.ts' --coveragePathIgnorePatterns='\.spec\.ts$' + run: cd eform-angular-frontend/eform-client && npx ng test --watch=false --include='src/app/plugins/modules/time-planning-pn/**/*.spec.ts' pn-playwright-test: needs: build runs-on: ubuntu-latest @@ -148,6 +137,8 @@ jobs: cp -av eform-angular-timeplanning-plugin/eform-client/playwright.config.ts eform-angular-frontend/eform-client/playwright.config.ts cp -av eform-angular-timeplanning-plugin/eform-client/cypress.config.ts eform-angular-frontend/eform-client/cypress.config.ts cd eform-angular-frontend/eform-client && ../../eform-angular-timeplanning-plugin/testinginstallpn.sh + - name: Enable Corepack + run: corepack enable - name: yarn install run: cd eform-angular-frontend/eform-client && yarn install - name: Install Playwright browsers diff --git a/.github/workflows/dotnet-core-pr.yml b/.github/workflows/dotnet-core-pr.yml index 0e2f0651f..9bfeab53d 100644 --- a/.github/workflows/dotnet-core-pr.yml +++ b/.github/workflows/dotnet-core-pr.yml @@ -18,7 +18,7 @@ jobs: with: fetch-depth: 0 repository: microting/eform-angular-frontend - ref: stable + ref: ${{ github.base_ref }} path: eform-angular-frontend - name: Copy dependencies run: | @@ -53,7 +53,7 @@ jobs: with: fetch-depth: 0 repository: microting/eform-angular-frontend - ref: stable + ref: ${{ github.base_ref }} path: eform-angular-frontend - name: Use Node.js uses: actions/setup-node@v3 @@ -69,23 +69,12 @@ jobs: cp -av eform-angular-timeplanning-plugin/eform-client/playwright/helpers eform-angular-frontend/eform-client/playwright/helpers cp -av eform-angular-timeplanning-plugin/eform-client/playwright.config.ts eform-angular-frontend/eform-client/playwright.config.ts cd eform-angular-frontend/eform-client && ../../eform-angular-timeplanning-plugin/testinginstallpn.sh + - name: Enable Corepack + run: corepack enable - name: yarn install run: cd eform-angular-frontend/eform-client && yarn install - name: Run Angular unit tests - run: | - cd eform-angular-frontend/eform-client - # Check if Jest is configured - if [ ! -f "jest.config.js" ] && [ ! -f "jest.config.ts" ]; then - echo "⚠️ Jest is not configured in the frontend repository." - echo "Unit tests require Jest to be configured. Skipping unit tests." - echo "" - echo "To enable unit tests, ensure Jest is configured in the frontend repository." - exit 0 - fi - - # Run Jest tests for time-planning-pn plugin - echo "Running Jest tests for time-planning-pn plugin..." - npm run test:unit -- --testPathPatterns=time-planning-pn --coverage --collectCoverageFrom='src/app/plugins/modules/time-planning-pn/**/*.ts' --coveragePathIgnorePatterns='\.spec\.ts$' + run: cd eform-angular-frontend/eform-client && npx ng test --watch=false --include='src/app/plugins/modules/time-planning-pn/**/*.spec.ts' pn-playwright-test: needs: build runs-on: ubuntu-latest @@ -124,7 +113,7 @@ jobs: uses: actions/checkout@v3 with: repository: microting/eform-angular-frontend - ref: stable + ref: ${{ github.base_ref }} path: eform-angular-frontend - name: Copy dependencies run: | @@ -137,6 +126,8 @@ jobs: cp -av eform-angular-timeplanning-plugin/eform-client/playwright.config.ts eform-angular-frontend/eform-client/playwright.config.ts cp -av eform-angular-timeplanning-plugin/eform-client/cypress.config.ts eform-angular-frontend/eform-client/cypress.config.ts cd eform-angular-frontend/eform-client && ../../eform-angular-timeplanning-plugin/testinginstallpn.sh + - name: Enable Corepack + run: corepack enable - name: yarn install run: cd eform-angular-frontend/eform-client && yarn install - name: Install Playwright browsers diff --git a/Dockerfile b/Dockerfile index 35167cb4c..5b89a9766 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ ARG DISABLE_SENTRY ENV PATH /app/node_modules/.bin:$PATH ENV DISABLE_SENTRY=${DISABLE_SENTRY} COPY eform-angular-frontend/eform-client ./ +RUN corepack enable RUN yarn install RUN yarn build diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.cy.ts index 6cef7974c..d44d7be45 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.cy.ts @@ -15,7 +15,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -36,7 +36,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/b/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/b/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/b/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/b/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.cy.ts index 5a3611ed0..594b86518 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.cy.ts @@ -426,7 +426,7 @@ describe('Dashboard assert', () => { pluginPage.Navbar.goToPluginsPage(); cy.task('log', '[Folder b - Dashboard Assert] Clicking action menu'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.task('log', '[Folder b - Dashboard Assert] Setting up intercept for settings-get'); diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.cy.ts index d41dadbf1..f7b8f1a63 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.cy.ts @@ -15,7 +15,7 @@ describe('Enable Backend Config plugin', () => { it('should validate default Time registration plugin settings', () => { cy.task('log', '[Folder b - Settings] ========== Test: Validate default Time registration plugin settings =========='); cy.task('log', '[Folder b - Settings] Clicking action menu'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.task('log', '[Folder b - Settings] Setting up intercept for settings-get'); @@ -138,7 +138,7 @@ describe('Enable Backend Config plugin', () => { it('should activate auto calculation break times', () => { cy.task('log', '[Folder b - Settings] ========== Test: Activate auto calculation break times =========='); cy.task('log', '[Folder b - Settings] Clicking action menu'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.task('log', '[Folder b - Settings] Setting up intercept for settings-get'); @@ -300,7 +300,7 @@ describe('Enable Backend Config plugin', () => { pluginPage.Navbar.goToPluginsPage(); cy.task('log', '[Folder b - Settings] Clicking action menu to verify settings'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.task('log', '[Folder b - Settings] Setting up intercept for settings-get'); @@ -363,7 +363,7 @@ describe('Enable Backend Config plugin', () => { cy.task('log', '[Folder b - Settings] ========== Test: Toggle GPS and Snapshot settings =========='); // Navigate to settings page cy.task('log', '[Folder b - Settings] Clicking action menu'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.task('log', '[Folder b - Settings] Setting up intercept for settings-get'); @@ -401,7 +401,7 @@ describe('Enable Backend Config plugin', () => { cy.task('log', '[Folder b - Settings] Reloading page to verify GPS persistence'); cy.visit('http://localhost:4200'); pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get-2'); @@ -437,7 +437,7 @@ describe('Enable Backend Config plugin', () => { // Reload page and verify GPS is still OFF cy.visit('http://localhost:4200'); pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get-3'); @@ -472,7 +472,7 @@ describe('Enable Backend Config plugin', () => { cy.task('log', '[Folder b - Settings] Reloading page to verify Snapshot persistence'); cy.visit('http://localhost:4200'); pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get-4'); @@ -508,7 +508,7 @@ describe('Enable Backend Config plugin', () => { cy.task('log', '[Folder b - Settings] Reloading page to verify Snapshot is still OFF'); cy.visit('http://localhost:4200'); pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get-5'); diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/c/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/c/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/c/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/c/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.cy.ts index 81280f7ef..49c42d33d 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.cy.ts @@ -237,7 +237,7 @@ describe('Dashboard edit values', () => { it('should enable auto break calculations with empty values', () => { pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get'); diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/d/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/d/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/d/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/d/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/e/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/e/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/e/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/e/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/f/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/f/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/f/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/f/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/g/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/g/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/g/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/g/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/h/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/h/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/h/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/h/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/i/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/i/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/i/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/i/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/j/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/j/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/j/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/j/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.cy.ts index b38473b26..29873d1e6 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.cy.ts @@ -13,7 +13,7 @@ describe('Dashboard edit values', () => { cy.task('log', '[Folder j - Auto Break Settings] Navigating to plugins page'); pluginPage.Navbar.goToPluginsPage(); cy.task('log', '[Folder j - Auto Break Settings] Clicking action menu'); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get'); @@ -231,7 +231,7 @@ describe('Dashboard edit values', () => { // cy.wait(500); // // pluginPage.Navbar.goToPluginsPage(); - // cy.get('#actionMenu') + // cy.get('[id^="actionMenu"]') // .should('be.visible') // .click({ force: true }); // cy.intercept('GET', '**/api/time-planning-pn/settings').as('settings-get'); diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/k/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/k/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/k/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/k/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/k/leave-policies.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/k/leave-policies.spec.cy.ts index 454ac31c0..371d49770 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/k/leave-policies.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/k/leave-policies.spec.cy.ts @@ -24,7 +24,7 @@ describe('Time Planning - Leave policies', () => { loginPage.login(); pluginPage.Navbar.goToPluginsPage(); - cy.get('#actionMenu') + cy.get('[id^="actionMenu"]') .scrollIntoView().should('be.visible') .click({ force: true }); diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/l/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/l/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/l/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/l/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/m/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/m/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/m/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/m/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/m/manager-assignment.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/m/manager-assignment.spec.cy.ts index 77285dd72..edc6d5479 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/m/manager-assignment.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/m/manager-assignment.spec.cy.ts @@ -14,11 +14,11 @@ describe('Time Planning - Manager Assignment', () => { // pluginPage.Navbar.goToPluginsPage(); // cy.get('body').then(($body) => { - // if ($body.find('#actionMenu').length === 0) { + // if ($body.find('[id^="actionMenu"]').length === 0) { // throw new Error('Plugin menu not available'); // } // - // cy.get('#actionMenu') + // cy.get('[id^="actionMenu"]') // .scrollIntoView().should('be.visible') // .click({ force: true }); // @@ -211,7 +211,7 @@ describe('Time Planning - Manager Assignment', () => { */ it('should toggle IsManager on and off and persist the state', () => { cy.get('body').then(($body) => { - if ($body.find('#actionMenu').length === 0) { + if ($body.find('[id^="actionMenu"]').length === 0) { cy.task('log', '[Time Planning Tests] Plugin menu not available - skipping test'); return; } @@ -305,7 +305,7 @@ describe('Time Planning - Manager Assignment', () => { */ it('should show tags field when manager checkbox is on and handle random text without errors', () => { cy.get('body').then(($body) => { - if ($body.find('#actionMenu').length === 0) { + if ($body.find('[id^="actionMenu"]').length === 0) { cy.task('log', '[Time Planning Tests] Plugin menu not available - skipping test'); return; } @@ -439,7 +439,7 @@ describe('Time Planning - Manager Assignment', () => { cy.task('log', `[Test 3] ========== Starting test - Create tag and persist selection at ${new Date().toISOString()} ==========`); cy.get('body').then(($body) => { - if ($body.find('#actionMenu').length === 0) { + if ($body.find('[id^="actionMenu"]').length === 0) { cy.task('log', '[Test 3] Plugin menu not available - skipping test'); return; } diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/n/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/n/activate-plugin.spec.cy.ts index e41ac4fdf..ac9f81814 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/n/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/n/activate-plugin.spec.cy.ts @@ -12,7 +12,7 @@ describe('Enable Backend Config plugin', () => { const pluginName = 'Microting Time Planning Plugin'; // Open action menu for the plugin - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); // Click the status button inside the menu to enable the plugin @@ -33,7 +33,7 @@ describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status - cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); + cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); cy.wait(500); cy.get('#plugin-status-button0') diff --git a/eform-client/cypress/e2e/plugins/time-planning-pn/o/activate-plugin.spec.cy.ts b/eform-client/cypress/e2e/plugins/time-planning-pn/o/activate-plugin.spec.cy.ts index d0c83d3bb..d31d55f28 100644 --- a/eform-client/cypress/e2e/plugins/time-planning-pn/o/activate-plugin.spec.cy.ts +++ b/eform-client/cypress/e2e/plugins/time-planning-pn/o/activate-plugin.spec.cy.ts @@ -16,7 +16,7 @@ // const pluginName = 'Microting Time Planning Plugin'; // // Open action menu for the plugin -// cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); +// cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); // cy.wait(500); // // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ // // Verify the plugin is enabled by checking the status // // Re-open the action menu to check the status -// cy.contains('.mat-mdc-row', pluginName).first().find('#actionMenu').click(); +// cy.contains('.mat-mdc-row', pluginName).first().find('[id^="actionMenu"]').click(); // cy.wait(500); // cy.get('#plugin-status-button0') diff --git a/eform-client/e2e/Tests/time-planning-settings/application-settings.plugins-page.spec.ts b/eform-client/e2e/Tests/time-planning-settings/application-settings.plugins-page.spec.ts index fe710bd72..d3992ff0e 100644 --- a/eform-client/e2e/Tests/time-planning-settings/application-settings.plugins-page.spec.ts +++ b/eform-client/e2e/Tests/time-planning-settings/application-settings.plugins-page.spec.ts @@ -19,7 +19,7 @@ describe('Application settings page - site header section', function () { expect(backendPlugin.version).equal('1.0.0.0'); // Open action menu to check status - const actionMenuBtn = await $('#action-items-0').$('#actionMenu'); + const actionMenuBtn = await $('#action-items-0').$('[id^="actionMenu"]'); await actionMenuBtn.waitForDisplayed({ timeout: 40000 }); await actionMenuBtn.waitForClickable({ timeout: 40000 }); await actionMenuBtn.click(); @@ -38,7 +38,7 @@ describe('Application settings page - site header section', function () { it('should activate the plugin', async () => { // Open action menu - const actionMenuBtn = await $('#action-items-0').$('#actionMenu'); + const actionMenuBtn = await $('#action-items-0').$('[id^="actionMenu"]'); await actionMenuBtn.waitForDisplayed({ timeout: 40000 }); await actionMenuBtn.waitForClickable({ timeout: 40000 }); await actionMenuBtn.click(); @@ -70,7 +70,7 @@ describe('Application settings page - site header section', function () { expect(backendPlugin.version).equal('1.0.0.0'); // Open action menu to check new status - const actionMenuBtn2 = await $('#action-items-0').$('#actionMenu'); + const actionMenuBtn2 = await $('#action-items-0').$('[id^="actionMenu"]'); await actionMenuBtn2.waitForDisplayed({ timeout: 40000 }); await actionMenuBtn2.waitForClickable({ timeout: 40000 }); await actionMenuBtn2.click(); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.ts index 62ea839ee..6a17d2a4f 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/a/time-planning-enabled.spec.ts @@ -16,7 +16,7 @@ test.describe('Enable Time Planning plugin', () => { const pluginName = 'Microting Time Planning Plugin'; await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await page.locator('#plugin-status-button0').scrollIntoViewIfNeeded(); @@ -34,7 +34,7 @@ test.describe('Enable Time Planning plugin', () => { await new PluginPage(page).Navbar.goToPluginsPage(); await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.ts index a13320d02..9be00c5ad 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-assert.spec.ts @@ -370,9 +370,9 @@ test.describe('Dashboard assert', () => { await pluginPage.Navbar.goToPluginsPage(); - await page.locator('#actionMenu').scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await page.locator('[id^="actionMenu"]').scrollIntoViewIfNeeded(); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const [settingsResp] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-a.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-a.spec.ts index 3399b5517..1c4fc465c 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-a.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-a.spec.ts @@ -205,7 +205,7 @@ async function clickTimePicker(page: import('@playwright/test').Page, timeStr: s await page.locator('[style="transform: rotateZ(' + degrees + 'deg) translateX(-50%);"] > span').click(); } if (minuteDegrees > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -267,7 +267,7 @@ test.describe('Dashboard edit values', () => { let minuteDegrees0 = 360 / 60 * parseInt(secondUpdatePlanTexts[i].plannedStartOfShift1.split(':')[1]); await page.locator('[style="transform: rotateZ(' + degrees0 + 'deg) translateX(-50%);"] > span').click(); if (minuteDegrees0 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees0 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees0 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="plannedStartOfShift1"]')).toHaveValue(secondUpdatePlanTexts[i].plannedStartOfShift1); @@ -282,7 +282,7 @@ test.describe('Dashboard edit values', () => { await page.locator('[style="transform: rotateZ(' + degrees1 + 'deg) translateX(-50%);"] > span').click(); } if (minuteDegrees1 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees1 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees1 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="plannedEndOfShift1"]')).toHaveValue(secondUpdatePlanTexts[i].plannedEndOfShift1); @@ -302,7 +302,7 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees2 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees2 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees2 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -319,7 +319,7 @@ test.describe('Dashboard edit values', () => { await page.locator('[style="transform: rotateZ(' + degrees3 + 'deg) translateX(-50%);"] > span').click(); } if (minuteDegrees3 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees3 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees3 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="plannedStartOfShift2"]')).toHaveValue(secondUpdatePlanTexts[i].plannedStartOfShift2); @@ -334,7 +334,7 @@ test.describe('Dashboard edit values', () => { await page.locator('[style="transform: rotateZ(' + degrees4 + 'deg) translateX(-50%);"] > span').click(); } if (minuteDegrees4 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees4 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees4 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="plannedEndOfShift2"]')).toHaveValue(secondUpdatePlanTexts[i].plannedEndOfShift2); @@ -353,7 +353,7 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees5 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees5 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees5 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="plannedBreakOfShift2"]')).toHaveValue(secondUpdatePlanTexts[i].plannedBreakOfShift2); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-b.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-b.spec.ts index a70632b1b..776f6b5c7 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-b.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/dashboard-edit-b.spec.ts @@ -167,7 +167,7 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees0 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees0 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees0 + 'deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -188,10 +188,10 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees1 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees1 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees1 + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (minuteDegrees1 === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -212,10 +212,10 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees2 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees2 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees2 + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (minuteDegrees2 === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="pause1Id"]')).toHaveValue(secondUpdateActualTexts[i].pause1Id); @@ -238,10 +238,10 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees3 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees3 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees3 + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (!minuteDegrees3 || isNaN(minuteDegrees3) || minuteDegrees3 === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -262,10 +262,10 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees4 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees4 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees4 + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (minuteDegrees4 === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); } @@ -286,10 +286,10 @@ test.describe('Dashboard edit values', () => { } } if (minuteDegrees5 > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees5 + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees5 + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (minuteDegrees5 === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator('[data-testid="pause2Id"]')).toHaveValue(secondUpdateActualTexts[i].pause2Id); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.ts index eef8bef20..a910928ff 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/b/time-planning-settings.spec.ts @@ -56,10 +56,10 @@ test.describe('Enable Backend Config plugin', () => { }); test('should validate default Time registration plugin settings', async ({ page }) => { - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const [settingsResponse] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), @@ -151,10 +151,10 @@ test.describe('Enable Backend Config plugin', () => { }); test('should activate auto calculation break times', async ({ page }) => { - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const [settingsResponse] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), @@ -261,10 +261,10 @@ test.describe('Enable Backend Config plugin', () => { await page.goto('http://localhost:4200'); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const [settingsResponse2] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), @@ -305,10 +305,10 @@ test.describe('Enable Backend Config plugin', () => { test('should toggle GPS and Snapshot settings and persist changes', async ({ page }) => { // Navigate to settings page - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const [settingsResponse] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), @@ -332,10 +332,10 @@ test.describe('Enable Backend Config plugin', () => { // Reload page and verify GPS is still ON await page.goto('http://localhost:4200'); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), page.locator('#plugin-settings-link0').click(), @@ -362,10 +362,10 @@ test.describe('Enable Backend Config plugin', () => { // Reload page and verify GPS is still OFF await page.goto('http://localhost:4200'); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), page.locator('#plugin-settings-link0').click(), @@ -390,10 +390,10 @@ test.describe('Enable Backend Config plugin', () => { // Reload page and verify Snapshot is still ON await page.goto('http://localhost:4200'); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), page.locator('#plugin-settings-link0').click(), @@ -419,10 +419,10 @@ test.describe('Enable Backend Config plugin', () => { // Reload page and verify Snapshot is still OFF await page.goto('http://localhost:4200'); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); await Promise.all([ page.waitForResponse(r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET'), page.locator('#plugin-settings-link0').click(), @@ -445,7 +445,7 @@ test.describe('Enable Backend Config plugin', () => { const isPath = (url: string, path: string) => new URL(url).pathname === path; const SETTINGS = '/api/time-planning-pn/settings'; const PAYROLL_SETTINGS = '/api/time-planning-pn/payroll/settings'; - const actionMenu = page.locator('#actionMenu'); + const actionMenu = page.locator('[id^="actionMenu"]'); const gpsToggle = page.locator('#gpsEnabledToggle'); const gpsChecked = gpsToggle.locator('div button'); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/c/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/c/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/c/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/c/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.ts index 0a27a557b..1a4afd6ff 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/c/dashboard-edit-a.spec.ts @@ -239,10 +239,10 @@ test.describe('Dashboard edit values', () => { test('should enable auto break calculations with empty values', async ({ page }) => { const pluginPage = new PluginPage(page); await pluginPage.Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const settingsGetPromise = page.waitForResponse( r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET' diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/c/time-planning-glsa-3f-pay-rules.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/c/time-planning-glsa-3f-pay-rules.spec.ts index 24e6b03ad..2a984bfaf 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/c/time-planning-glsa-3f-pay-rules.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/c/time-planning-glsa-3f-pay-rules.spec.ts @@ -195,9 +195,9 @@ async function setTimepickerValue( // Select minute on the clock face const minuteDegrees = 360 / 60 * minutes; if (minuteDegrees === 0) { - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click(); } else { - await page.locator(`[style="transform: rotateZ(${minuteDegrees}deg) translateX(-50%);"] > span`).click({ force: true }); + await page.locator(`ngx-material-timepicker-minutes-face [style="transform: rotateZ(${minuteDegrees}deg) translateX(-50%);"] > span`).click({ force: true }); } // Confirm @@ -467,7 +467,7 @@ test.describe('GLS-A / 3F Pay Rule Set Full Pipeline E2E', () => { await expect(row.first()).toBeVisible({ timeout: 10000 }); // Open the row-action menu and assert Edit + Delete are disabled - await row.first().locator('#actionMenu').click(); + await row.first().locator('[id^="actionMenu"]').click(); const editBtn = page.locator('button[id^="editPayRuleSetBtn-"]'); const deleteBtn = page.locator('button[id^="deletePayRuleSetBtn-"]'); await expect(editBtn).toBeVisible({ timeout: 5000 }); @@ -480,7 +480,7 @@ test.describe('GLS-A / 3F Pay Rule Set Full Pipeline E2E', () => { // the modal itself must render the locked summary view (no editable // name field, no Update button). // Force-click the disabled edit button via JS to verify modal behavior. - await row.first().locator('#actionMenu').click(); + await row.first().locator('[id^="actionMenu"]').click(); await page.evaluate(() => { const btn = document.querySelector('button[id^="editPayRuleSetBtn-"]') as HTMLButtonElement | null; if (btn) { diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/d/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/d/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/d/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/d/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/e/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/e/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/e/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/e/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/f/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/f/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/f/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/f/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/g/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/g/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/g/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/g/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/h/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/h/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/h/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/h/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/i/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/i/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/i/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/i/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/j/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/j/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/j/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/j/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.ts index e21504546..a34b350c1 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/j/dashboard-edit-a.spec.ts @@ -16,9 +16,9 @@ test.describe('Dashboard edit values', () => { await new LoginPage(page).login(); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu').scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await page.locator('[id^="actionMenu"]').scrollIntoViewIfNeeded(); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const settingsGetPromise = page.waitForResponse( r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET' diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/j1m/dashboard-edit-a.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/j1m/dashboard-edit-a.spec.ts index 0689c1830..0d0ccf36a 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/j1m/dashboard-edit-a.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/j1m/dashboard-edit-a.spec.ts @@ -49,9 +49,9 @@ test.describe('Dashboard edit values (j1m, flag-on, global values round-trip)', await new LoginPage(page).login(); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu').scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await page.locator('[id^="actionMenu"]').scrollIntoViewIfNeeded(); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const settingsGetPromise = page.waitForResponse( r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET' diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/k/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/k/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/k/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/k/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/k/leave-policies.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/k/leave-policies.spec.ts index 532067f02..bb13f9111 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/k/leave-policies.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/k/leave-policies.spec.ts @@ -25,10 +25,10 @@ test.describe('Time Planning - Leave policies', () => { const pluginPage = new PluginPage(page); await pluginPage.Navbar.goToPluginsPage(); - await page.locator('#actionMenu') + await page.locator('[id^="actionMenu"]') .scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); const planningsIndexResponse = page.waitForResponse( (resp) => resp.url().includes('/api/time-planning-pn/plannings/index') && resp.status() === 200 diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/l/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/l/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/l/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/l/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/l1m/dashboard-edit-actual-exact.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/l1m/dashboard-edit-actual-exact.spec.ts index 90c7c445b..898ea7c52 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/l1m/dashboard-edit-actual-exact.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/l1m/dashboard-edit-actual-exact.spec.ts @@ -87,7 +87,10 @@ async function openDialogForActiveCell(page: Page) { await page.locator('#cell0_0').click(); // Wipe any seed-prefilled shift-1 actual-start so the test starts from a - // known-empty pair. Mirrors the f1m beforeEach wipe. + // known-empty pair. Mirrors the f1m beforeEach wipe. Wait for the dialog + // first: count() does not wait, and the dialog renders a frame after the + // click, so counting straight away skipped the wipe. + await page.locator('[data-testid="start1StartedAt"]').waitFor({ state: 'visible', timeout: 15000 }); const wipeBtn = page.locator('[data-testid="start1StartedAt"]') .locator('xpath=ancestor::div[contains(@class,"flex-row")]') .locator('button mat-icon') diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/m/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/m/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/m/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/m/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/m/manager-assignment.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/m/manager-assignment.spec.ts index 6d3ad53c8..534597595 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/m/manager-assignment.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/m/manager-assignment.spec.ts @@ -137,7 +137,7 @@ test.describe('Time Planning - Manager Assignment', () => { * Test 1: Check/uncheck IsManager and verify it persists */ test('should toggle IsManager on and off and persist the state', async ({ page }) => { - if (await page.locator('#actionMenu').count() === 0) { + if (await page.locator('[id^="actionMenu"]').count() === 0) { return; } @@ -202,7 +202,7 @@ test.describe('Time Planning - Manager Assignment', () => { * Test 2: Verify tags field shows when manager is on, test with random text */ test('should show tags field when manager checkbox is on and handle random text without errors', async ({ page }) => { - if (await page.locator('#actionMenu').count() === 0) { + if (await page.locator('[id^="actionMenu"]').count() === 0) { return; } @@ -270,7 +270,7 @@ test.describe('Time Planning - Manager Assignment', () => { * Test 3: Create a tag, use it in assigned-site-modal, and verify it persists */ test('should create a tag, use it in assigned-site-modal, and persist the selection', async ({ page }) => { - if (await page.locator('#actionMenu').count() === 0) { + if (await page.locator('[id^="actionMenu"]').count() === 0) { return; } diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/n/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/n/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/n/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/n/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/o/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/o/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/o/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/o/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/p/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/p/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/p/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/p/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/p/payroll-export.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/p/payroll-export.spec.ts index aad92ab8d..5c9f33bc6 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/p/payroll-export.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/p/payroll-export.spec.ts @@ -15,9 +15,9 @@ test.describe('Payroll export', () => { r => r.url().includes('/api/time-planning-pn/settings') && r.request().method() === 'GET' ); await new PluginPage(page).Navbar.goToPluginsPage(); - await page.locator('#actionMenu').scrollIntoViewIfNeeded(); - await expect(page.locator('#actionMenu')).toBeVisible(); - await page.locator('#actionMenu').click({ force: true }); + await page.locator('[id^="actionMenu"]').scrollIntoViewIfNeeded(); + await expect(page.locator('[id^="actionMenu"]')).toBeVisible(); + await page.locator('[id^="actionMenu"]').click({ force: true }); await page.locator('#plugin-settings-link0').click(); await settingsGetPromise; await page.waitForTimeout(2000); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/q/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/q/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/q/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/q/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/r/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/r/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/r/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/r/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/r/dashboard-version-history.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/r/dashboard-version-history.spec.ts index ef83c61f9..b63ef36ee 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/r/dashboard-version-history.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/r/dashboard-version-history.spec.ts @@ -61,7 +61,7 @@ async function pickPlannedTime(page: Page, testid: string, timeStr: string) { await page.locator('[style="transform: rotateZ(' + degrees + 'deg) translateX(-50%);"] > span').click(); } if (minuteDegrees > 0) { - await page.locator('[style="transform: rotateZ(' + minuteDegrees + 'deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(' + minuteDegrees + 'deg) translateX(-50%);"] > span').click({ force: true }); } if (minuteDegrees === 0) { // The minute face must be clicked even for :00 — skipping it keeps the @@ -71,7 +71,7 @@ async function pickPlannedTime(page: Page, testid: string, timeStr: string) { // clock-face__number--outer div intercepts pointer events on the "00" // span, so a plain click retries until the test timeout (CI run // 29317211838). - await page.locator('[style="transform: rotateZ(360deg) translateX(-50%);"] > span').click({ force: true }); + await page.locator('ngx-material-timepicker-minutes-face [style="transform: rotateZ(360deg) translateX(-50%);"] > span').click({ force: true }); } await page.locator('.timepicker-button span').filter({ hasText: 'Ok' }).click(); await expect(page.locator(`[data-testid="${testid}"]`)).toHaveValue(timeStr); diff --git a/eform-client/playwright/e2e/plugins/time-planning-pn/s/activate-plugin.spec.ts b/eform-client/playwright/e2e/plugins/time-planning-pn/s/activate-plugin.spec.ts index 05e9ede65..c86c5f63d 100644 --- a/eform-client/playwright/e2e/plugins/time-planning-pn/s/activate-plugin.spec.ts +++ b/eform-client/playwright/e2e/plugins/time-planning-pn/s/activate-plugin.spec.ts @@ -15,7 +15,7 @@ test.describe('Enable Backend Config plugin', () => { // Open action menu for the plugin await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); // Click the status button inside the menu to enable the plugin @@ -37,7 +37,7 @@ test.describe('Enable Backend Config plugin', () => { // Verify the plugin is enabled by checking the status // Re-open the action menu to check the status await page.locator('.mat-mdc-row').filter({ hasText: pluginName }).first() - .locator('#actionMenu').click(); + .locator('[id^="actionMenu"]').click(); await page.waitForTimeout(500); await expect( diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/day-lock.util.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/day-lock.util.spec.ts index f2cc3529c..9adc6e731 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/day-lock.util.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/day-lock.util.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, vi} from 'vitest'; import {DatePipe} from '@angular/common'; import {EMPTY, of, Subject, throwError} from 'rxjs'; import { @@ -189,7 +190,7 @@ describe('day-lock util', () => { }); describe('formatReconciledProvenance', () => { - const translate = {instant: jest.fn((key: string, params?: object) => key)} as any; + const translate = {instant: vi.fn((key: string, params?: object) => key)} as any; /** What the server sends after the projection tags it: a UTC instant, "Z" and all. */ const SERVER_STAMP = '2026-09-14T10:32:11Z'; @@ -200,7 +201,7 @@ describe('day-lock util', () => { // passed, so DatePipe renders it in the VIEWER's zone -- which is the point. // Passing 'UTC' would pin every viewer to the server's clock instead. const datePipe = new DatePipe('en-US'); - const transform = jest.spyOn(datePipe, 'transform'); + const transform = vi.spyOn(datePipe, 'transform'); formatReconciledProvenance(SERVER_STAMP, datePipe, translate); @@ -315,30 +316,30 @@ describe('day-lock util', () => { }); it('stops waiting for a request that never answers, and calls that unknown', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const seen: LockRequestOutcome[] = []; const request = new Subject(); sendLockRequest(request, outcome => seen.push(outcome)); expect(seen).toEqual([]); - jest.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); + vi.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); expect(seen).toEqual([{kind: 'unknown'}]); - jest.useRealTimers(); + vi.useRealTimers(); }); it('lets no late answer through, because the abort is from this end only', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const seen: LockRequestOutcome[] = []; const request = new Subject(); sendLockRequest(request, outcome => seen.push(outcome)); - jest.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); + vi.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); request.next({success: true}); request.complete(); expect(seen).toEqual([{kind: 'unknown'}]); - jest.useRealTimers(); + vi.useRealTimers(); }); }); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/pause-segments.util.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/pause-segments.util.spec.ts index 06314abe9..99e897bf5 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/pause-segments.util.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/pause-segments.util.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { buildPauseSegments } from './pause-segments.util'; describe('buildPauseSegments', () => { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.spec.ts index 2a3acf5a1..17b6952db 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.spec.ts @@ -1,3 +1,5 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AssignedSiteDialogComponent } from './assigned-site-dialog.component'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; @@ -5,16 +7,15 @@ import { FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { TimePlanningPnSettingsService, TimePlanningPnPayRuleSetsService } from '../../../../services'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('AssignedSiteDialogComponent', () => { let component: AssignedSiteDialogComponent; let fixture: ComponentFixture; - let mockSettingsService: jest.Mocked; - let mockPayRuleSetsService: jest.Mocked; - let mockStore: jest.Mocked; + let mockSettingsService: Mocked; + let mockPayRuleSetsService: Mocked; + let mockStore: Mocked; const mockAssignedSiteData = { id: 1, @@ -58,16 +59,16 @@ describe('AssignedSiteDialogComponent', () => { beforeEach(async () => { mockSettingsService = { - getGlobalAutoBreakCalculationSettings: jest.fn(), - updateAssignedSite: jest.fn(), - getAssignedSite: jest.fn(), - getAvailableTags: jest.fn(), + getGlobalAutoBreakCalculationSettings: vi.fn(), + updateAssignedSite: vi.fn(), + getAssignedSite: vi.fn(), + getAvailableTags: vi.fn(), } as any; mockPayRuleSetsService = { - getPayRuleSets: jest.fn(), + getPayRuleSets: vi.fn(), } as any; mockStore = { - select: jest.fn(), + select: vi.fn(), } as any; mockStore.select.mockReturnValue(of(true)); @@ -82,9 +83,7 @@ describe('AssignedSiteDialogComponent', () => { ); await TestBed.configureTestingModule({ - declarations: [AssignedSiteDialogComponent], - imports: [ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], + imports: [AssignedSiteDialogComponent, ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], providers: [ FormBuilder, { provide: MAT_DIALOG_DATA, useValue: mockAssignedSiteData }, @@ -272,7 +271,7 @@ describe('AssignedSiteDialogComponent', () => { }); it('should handle tags loading error gracefully', () => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined); mockSettingsService.getAvailableTags.mockReturnValue( of({ success: false, model: null }) as any ); @@ -466,9 +465,7 @@ describe('AssignedSiteDialogComponent', () => { TestBed.resetTestingModule(); TestBed.configureTestingModule({ - declarations: [AssignedSiteDialogComponent], - imports: [ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], + imports: [AssignedSiteDialogComponent, ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], providers: [ FormBuilder, { provide: MAT_DIALOG_DATA, useValue: dataWithManager }, @@ -499,8 +496,8 @@ describe('AssignedSiteDialogComponent', () => { // and (b) onEntryMethodChange does not mutate editing-policy state. // // Sibling tests in this file deliberately avoid `fixture.detectChanges()` - // because the dialog's template uses Material form-control directives whose - // value accessors are not registered under NO_ERRORS_SCHEMA. We follow the + // because, when the bed declared the dialog under NO_ERRORS_SCHEMA, its + // Material form-control value accessors were not registered. We follow the // same pattern here: assert against component state, not the rendered DOM. function setupComponentWithData( @@ -509,9 +506,7 @@ describe('AssignedSiteDialogComponent', () => { const data = { ...mockAssignedSiteData, ...overrides }; TestBed.resetTestingModule(); TestBed.configureTestingModule({ - declarations: [AssignedSiteDialogComponent], - imports: [ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], + imports: [AssignedSiteDialogComponent, ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], providers: [ FormBuilder, { provide: MAT_DIALOG_DATA, useValue: data }, @@ -606,10 +601,9 @@ describe('AssignedSiteDialogComponent', () => { describe('1-minute intervals (no longer editable)', () => { // We follow the same no-`detectChanges` pattern adopted in PR #1538: // assert against component state and observables, not the rendered DOM. - // Rendering the dialog under NO_ERRORS_SCHEMA hits the - // NG01203 "No value accessor for form control name: 'resigned'" issue - // because Material form-control directives aren't registered in the - // tested module. + // Rendering the dialog when the bed declared it under NO_ERRORS_SCHEMA hit + // NG01203 "No value accessor for form control name: 'resigned'", because + // Material form-control directives weren't registered in the tested module. function setupWithData( overrides: Partial, @@ -617,9 +611,7 @@ describe('AssignedSiteDialogComponent', () => { const data = { ...mockAssignedSiteData, ...overrides }; TestBed.resetTestingModule(); TestBed.configureTestingModule({ - declarations: [AssignedSiteDialogComponent], - imports: [ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], + imports: [AssignedSiteDialogComponent, ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], providers: [ FormBuilder, { provide: MAT_DIALOG_DATA, useValue: data }, @@ -646,8 +638,7 @@ describe('AssignedSiteDialogComponent', () => { // The checkbox's *ngIf is `data.usePunchClock` inside the mobile-axis block // (gated by !data.resigned && data.allowPersonalTimeRegistration && admin). // Same no-`detectChanges` pattern as the sibling blocks above: assert - // against component state, not the rendered DOM, because Material - // form-control directives aren't registered under NO_ERRORS_SCHEMA. + // against component state, not the rendered DOM (see the first block). function setupWithData( overrides: Partial & { overMidnight?: boolean }, @@ -655,9 +646,7 @@ describe('AssignedSiteDialogComponent', () => { const data = { ...mockAssignedSiteData, ...overrides }; TestBed.resetTestingModule(); TestBed.configureTestingModule({ - declarations: [AssignedSiteDialogComponent], - imports: [ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], + imports: [AssignedSiteDialogComponent, ReactiveFormsModule, TranslateModule.forRoot(), HttpClientTestingModule], providers: [ FormBuilder, { provide: MAT_DIALOG_DATA, useValue: data }, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.ts index 0e3448d17..322548955 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/assigned-site/assigned-site-dialog.component.ts @@ -1,10 +1,7 @@ import {Component, DoCheck, OnInit, inject } from '@angular/core'; -import { - MAT_DIALOG_DATA, - MatDialog -} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; import {AssignedSiteModel, CommonTagModel, GlobalAutoBreakSettingsModel, PayRuleSetSimpleModel} from '../../../../models'; import {PayRuleSetsViewModalComponent} from '../../../../modules/pay-rule-sets/components/pay-rule-sets-view-modal/pay-rule-sets-view-modal.component'; import {selectCurrentUserIsAdmin, selectCurrentUserIsFirstUser} from 'src/app/state'; @@ -19,13 +16,26 @@ import { ReactiveFormsModule, FormControl, } from '@angular/forms'; +import {NgIf, NgFor, AsyncPipe} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatFormField, MatLabel, MatError, MatHint, MatSuffix} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatDatepicker, MatDatepickerInput, MatDatepickerToggle} from '@angular/material/datepicker'; +import {MatCheckbox} from '@angular/material/checkbox'; +import {MatRadioGroup, MatRadioButton} from '@angular/material/radio'; +import {MatTabGroup, MatTab} from '@angular/material/tabs'; +import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; @Component({ selector: 'app-assigned-site-dialog', + imports: [NgIf, NgFor, AsyncPipe, ReactiveFormsModule, TranslatePipe, MatIcon, MatIconButton, MatFormField, MatLabel, MatError, MatHint, MatSuffix, MatInput, MtxSelect, MatTooltip, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDatepicker, MatDatepickerInput, MatDatepickerToggle, MatCheckbox, MatRadioGroup, MatRadioButton, MatTabGroup, MatTab, NgxMaterialTimepickerModule], templateUrl: './assigned-site-dialog.component.html', styleUrls: ['./assigned-site-dialog.component.scss'], - standalone: false }) export class AssignedSiteDialogComponent implements DoCheck, OnInit { private fb = inject(FormBuilder); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.spec.ts index d0bb91246..856bb2a98 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.spec.ts @@ -1,3 +1,6 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked, Mock} from 'vitest'; +import { provideNativeDateAdapter } from '@angular/material/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DownloadExcelDialogComponent, DownloadExcelDialogData } from './download-excel-dialog.component'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; @@ -5,7 +8,6 @@ import { TimePlanningPnPlanningsService, TimePlanningPnWorkingHoursService } fro import { ToastrService } from 'ngx-toastr'; import { of, throwError } from 'rxjs'; import { format } from 'date-fns'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; @@ -13,9 +15,9 @@ import { CommonModule } from '@angular/common'; describe('DownloadExcelDialogComponent', () => { let component: DownloadExcelDialogComponent; let fixture: ComponentFixture; - let mockWorkingHoursService: jest.Mocked; - let mockPlanningsService: jest.Mocked; - let mockToastrService: jest.Mocked; + let mockWorkingHoursService: Mocked; + let mockPlanningsService: Mocked; + let mockToastrService: Mocked; let dialogData: DownloadExcelDialogData; /** Two active workers on tag 1, one on tag 2, and a resigned one that also carries tag 1. */ @@ -28,26 +30,26 @@ describe('DownloadExcelDialogComponent', () => { beforeEach(async () => { // Mock URL.createObjectURL and URL.revokeObjectURL for file-saver - global.URL.createObjectURL = jest.fn(() => 'mock-url'); - global.URL.revokeObjectURL = jest.fn(); + global.URL.createObjectURL = vi.fn(() => 'mock-url'); + global.URL.revokeObjectURL = vi.fn(); // Mock HTMLAnchorElement.prototype.click to prevent navigation errors - const mockClick = jest.fn(); + const mockClick = vi.fn(); Object.defineProperty(HTMLAnchorElement.prototype, 'click', { configurable: true, value: mockClick, }); mockWorkingHoursService = { - downloadReport: jest.fn(), - downloadReportAllWorkers: jest.fn(), + downloadReport: vi.fn(), + downloadReportAllWorkers: vi.fn(), } as any; mockPlanningsService = { - getSiteTags: jest.fn().mockReturnValue(of({ success: true, model: siteTags })), + getSiteTags: vi.fn().mockReturnValue(of({ success: true, model: siteTags })), } as any; mockToastrService = { - error: jest.fn(), - success: jest.fn(), + error: vi.fn(), + success: vi.fn(), } as any; dialogData = { @@ -60,10 +62,9 @@ describe('DownloadExcelDialogComponent', () => { }; await TestBed.configureTestingModule({ - declarations: [DownloadExcelDialogComponent], - imports: [CommonModule, FormsModule, TranslateModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [DownloadExcelDialogComponent, CommonModule, FormsModule, TranslateModule.forRoot()], providers: [ + provideNativeDateAdapter(), { provide: MAT_DIALOG_DATA, useValue: dialogData }, { provide: TimePlanningPnWorkingHoursService, useValue: mockWorkingHoursService }, { provide: TimePlanningPnPlanningsService, useValue: mockPlanningsService }, @@ -188,7 +189,7 @@ describe('DownloadExcelDialogComponent', () => { }); }); - it('should show error toast when download fails', (done) => { + it('should show error toast when download fails', async () => { mockWorkingHoursService.downloadReport.mockReturnValue( throwError(() => new Error('Download failed')) ); @@ -196,10 +197,8 @@ describe('DownloadExcelDialogComponent', () => { component.onDownloadExcelReport(); // Wait a bit for async operations - setTimeout(() => { - expect(mockToastrService.error).toHaveBeenCalledWith('Error downloading report'); - done(); - }, 100); + await new Promise((resolve) => setTimeout(resolve, 100)); + expect(mockToastrService.error).toHaveBeenCalledWith('Error downloading report'); }); }); @@ -233,7 +232,7 @@ describe('DownloadExcelDialogComponent', () => { expect('tagIds' in callArgs).toBe(false); }); - it('should show error toast when download all workers fails', (done) => { + it('should show error toast when download all workers fails', async () => { mockWorkingHoursService.downloadReportAllWorkers.mockReturnValue( throwError(() => new Error('Download failed')) ); @@ -241,10 +240,8 @@ describe('DownloadExcelDialogComponent', () => { component.onDownloadExcelReportAllWorkers(); // Wait a bit for async operations - setTimeout(() => { - expect(mockToastrService.error).toHaveBeenCalledWith('Error downloading report'); - done(); - }, 100); + await new Promise((resolve) => setTimeout(resolve, 100)); + expect(mockToastrService.error).toHaveBeenCalledWith('Error downloading report'); }); it('should not include siteId in all workers report model', () => { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.ts index b53b05d6f..536169a41 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/download-excel/download-excel-dialog.component.ts @@ -2,7 +2,7 @@ import {Component, OnDestroy, OnInit, inject } from '@angular/core'; import {EMPTY, Subscription} from 'rxjs'; -import {MatDatepickerInputEvent} from '@angular/material/datepicker'; +import {MatDatepickerInputEvent, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate} from '@angular/material/datepicker'; import {SiteDto} from 'src/app/common/models'; import { CommonTagModel, @@ -18,7 +18,13 @@ import { TimePlanningPnWorkingHoursService } from 'src/app/plugins/modules/time-planning-pn/services'; import {ToastrService} from 'ngx-toastr'; -import {MAT_DIALOG_DATA} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; +import {NgIf} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel, MatPrefix} from '@angular/material/form-field'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatTooltip} from '@angular/material/tooltip'; /** * The filters the planning page has on screen when the dialog is opened. The export @@ -36,9 +42,9 @@ export interface DownloadExcelDialogData { @Component({ selector: 'app-download-excel-dialog', + imports: [NgIf, FormsModule, TranslatePipe, MatFormField, MatLabel, MatPrefix, MtxSelect, MatTooltip, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate], templateUrl: './download-excel-dialog.component.html', styleUrls: ['./download-excel-dialog.component.scss'], - standalone: false, }) export class DownloadExcelDialogComponent implements OnInit, OnDestroy { private data = inject(MAT_DIALOG_DATA); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.spec.ts index a54c76d2f..8b4c62bea 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.spec.ts @@ -1,5 +1,7 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; +import { provideNativeDateAdapter } from '@angular/material/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { TranslateModule } from '@ngx-translate/core'; import { HttpClientTestingModule } from '@angular/common/http/testing'; @@ -11,19 +13,19 @@ import { of } from 'rxjs'; describe('PayrollExportDialogComponent', () => { let component: PayrollExportDialogComponent; let fixture: ComponentFixture; - let mockDialogRef: jest.Mocked>; - let mockPayrollExportService: jest.Mocked; + let mockDialogRef: Mocked>; + let mockPayrollExportService: Mocked; const mockDialogData = { cutoffDay: 19, payrollSystem: 1 }; beforeEach(async () => { mockDialogRef = { - close: jest.fn(), + close: vi.fn(), } as any; mockPayrollExportService = { - preview: jest.fn(), - exportPayroll: jest.fn(), + preview: vi.fn(), + exportPayroll: vi.fn(), } as any; mockPayrollExportService.preview.mockReturnValue( @@ -31,10 +33,9 @@ describe('PayrollExportDialogComponent', () => { ); await TestBed.configureTestingModule({ - declarations: [PayrollExportDialogComponent], - imports: [TranslateModule.forRoot(), HttpClientTestingModule, ToastrModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [PayrollExportDialogComponent, TranslateModule.forRoot(), HttpClientTestingModule, ToastrModule.forRoot()], providers: [ + provideNativeDateAdapter(), { provide: MAT_DIALOG_DATA, useValue: mockDialogData }, { provide: MatDialogRef, useValue: mockDialogRef }, { provide: TimePlanningPnPayrollExportService, useValue: mockPayrollExportService }, @@ -53,7 +54,7 @@ describe('PayrollExportDialogComponent', () => { it('should calculate period when before cutoff day', () => { // Simulate a date before the cutoff (e.g., April 10 with cutoff 19) const fakeNow = new Date(2026, 3, 10); // April 10, 2026 - jest.spyOn(global, 'Date').mockImplementation((...args: any[]) => { + vi.spyOn(global, 'Date').mockImplementation((...args: any[]) => { if (args.length === 0) { return fakeNow; } @@ -62,7 +63,7 @@ describe('PayrollExportDialogComponent', () => { }); const OriginalDate = global.Date; // Restore Date before calling ngOnInit - jest.restoreAllMocks(); + vi.restoreAllMocks(); // Directly set a known date scenario by calling ngOnInit with controlled state // Since we cannot easily mock Date constructor used internally, @@ -95,10 +96,9 @@ describe('PayrollExportDialogComponent', () => { // Reconfigure with no cutoffDay TestBed.resetTestingModule(); await TestBed.configureTestingModule({ - declarations: [PayrollExportDialogComponent], - imports: [TranslateModule.forRoot(), HttpClientTestingModule, ToastrModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [PayrollExportDialogComponent, TranslateModule.forRoot(), HttpClientTestingModule, ToastrModule.forRoot()], providers: [ + provideNativeDateAdapter(), { provide: MAT_DIALOG_DATA, useValue: { cutoffDay: 0, payrollSystem: 1 } }, { provide: MatDialogRef, useValue: mockDialogRef }, { provide: TimePlanningPnPayrollExportService, useValue: mockPayrollExportService }, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.ts index 4a358fc30..4531c81fb 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/payroll-export/payroll-export-dialog.component.ts @@ -1,9 +1,16 @@ import { Component, Inject, OnInit, inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { TimePlanningPnPayrollExportService } from '../../../../services'; import { ToastrService } from 'ngx-toastr'; import { saveAs } from 'file-saver'; import { format } from 'date-fns'; +import {NgIf} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatFormField, MatLabel, MatSuffix} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MatDatepicker, MatDatepickerInput, MatDatepickerToggle} from '@angular/material/datepicker'; export interface PayrollExportDialogData { cutoffDay: number; @@ -12,8 +19,8 @@ export interface PayrollExportDialogData { @Component({ selector: 'app-payroll-export-dialog', + imports: [NgIf, FormsModule, TranslatePipe, MatIcon, MatFormField, MatLabel, MatSuffix, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions, MatDatepicker, MatDatepickerInput, MatDatepickerToggle], templateUrl: './payroll-export-dialog.component.html', - standalone: false, }) export class PayrollExportDialogComponent implements OnInit { private payrollExportService = inject(TimePlanningPnPayrollExportService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/version-history-modal/version-history-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/version-history-modal/version-history-modal.component.ts index f1b9aa680..0c9fed2dd 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/version-history-modal/version-history-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/version-history-modal/version-history-modal.component.ts @@ -1,11 +1,12 @@ import {Component, OnInit, inject, OnDestroy} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent} from '@angular/material/dialog'; import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser'; import {TimePlanningPnPlanningsService} from '../../../../services'; import {PlanRegistrationVersionHistoryModel, FieldChangeModel} from '../../../../models'; import {TemplateFilesService} from 'src/app/common/services'; import {Subscription} from 'rxjs'; -import {TranslateService} from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; +import {NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault} from '@angular/common'; const GOOGLE_MAPS_EMBED_URL = 'https://www.google.com/maps?q={lat},{lng}&output=embed'; const PICTURE_SNAPSHOT_API_URL = '/api/template-files/get-image/'; @@ -32,9 +33,9 @@ interface TimelineDay { @Component({ selector: 'app-version-history-modal', + imports: [NgIf, NgFor, NgSwitch, NgSwitchCase, NgSwitchDefault, TranslatePipe, MatDialogTitle, MatDialogContent], templateUrl: './version-history-modal.component.html', styleUrls: ['./version-history-modal.component.scss'], - standalone: false }) export class VersionHistoryModalComponent implements OnInit, OnDestroy { versionHistory: PlanRegistrationVersionHistoryModel; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.spec.ts index efdf2377b..bb8da76b0 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.spec.ts @@ -1,5 +1,8 @@ +import {describe, it, expect, beforeEach, afterEach, beforeAll, afterAll, vi} from 'vitest'; +import type {Mocked, Mock} from 'vitest'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { MODULE_ROOT } from '../../../../testing/source-paths'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { WorkdayEntityDialogComponent } from './workday-entity-dialog.component'; import { LOCK_REQUEST_TIMEOUT_MS } from '../../day-lock.util'; @@ -9,7 +12,6 @@ import { TimePlanningPnPlanningsService, TimePlanningPnGpsCoordinatesService, Ti import { TranslateService } from '@ngx-translate/core'; import { DatePipe, CommonModule } from '@angular/common'; import { EMPTY, of, Subject, throwError } from 'rxjs'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; @@ -23,14 +25,14 @@ import { ToastrService } from 'ngx-toastr'; describe('WorkdayEntityDialogComponent', () => { let component: WorkdayEntityDialogComponent; let fixture: ComponentFixture; - let mockPlanningsService: jest.Mocked; - let mockTranslateService: jest.Mocked; - let mockGpsCoordinatesService: jest.Mocked; - let mockPictureSnapshotsService: jest.Mocked; - let mockDomSanitizer: jest.Mocked; - let mockTemplateFilesService: jest.Mocked; - let mockDialogRef: jest.Mocked>; - let mockToastrService: jest.Mocked; + let mockPlanningsService: Mocked; + let mockTranslateService: Mocked; + let mockGpsCoordinatesService: Mocked; + let mockPictureSnapshotsService: Mocked; + let mockDomSanitizer: Mocked; + let mockTemplateFilesService: Mocked; + let mockDialogRef: Mocked>; + let mockToastrService: Mocked; const mockData = { planningPrDayModels: { @@ -102,33 +104,33 @@ describe('WorkdayEntityDialogComponent', () => { beforeEach(async () => { mockPlanningsService = { - updatePlanning: jest.fn(), - reconcileDay: jest.fn(), - unreconcileDay: jest.fn(), + updatePlanning: vi.fn(), + reconcileDay: vi.fn(), + unreconcileDay: vi.fn(), } as any; mockTranslateService = { - instant: jest.fn(), - stream: jest.fn(), + instant: vi.fn(), + stream: vi.fn(), onLangChange: of({ lang: 'en' }), } as any; mockGpsCoordinatesService = { - getByPlanRegistrationId: jest.fn().mockReturnValue(of({ success: false, model: null })), + getByPlanRegistrationId: vi.fn().mockReturnValue(of({ success: false, model: null })), } as any; mockPictureSnapshotsService = { - getByPlanRegistrationId: jest.fn().mockReturnValue(of({ success: false, model: null })), + getByPlanRegistrationId: vi.fn().mockReturnValue(of({ success: false, model: null })), } as any; mockDomSanitizer = { - bypassSecurityTrustResourceUrl: jest.fn().mockImplementation((url) => url), + bypassSecurityTrustResourceUrl: vi.fn().mockImplementation((url) => url), } as any; mockTemplateFilesService = { - getImage: jest.fn().mockReturnValue(of(new Blob())), + getImage: vi.fn().mockReturnValue(of(new Blob())), } as any; mockToastrService = { - error: jest.fn(), - success: jest.fn(), + error: vi.fn(), + success: vi.fn(), } as any; mockDialogRef = { - close: jest.fn(), + close: vi.fn(), // The reconcile/unlock PUTs flip this while they are in flight, so it has to // start as the real MatDialogRef default rather than undefined. disableClose: false, @@ -144,9 +146,7 @@ describe('WorkdayEntityDialogComponent', () => { mockTranslateService.stream.mockReturnValue(of('Translated')); await TestBed.configureTestingModule({ - declarations: [WorkdayEntityDialogComponent], - imports: [CommonModule, ReactiveFormsModule, TranslateModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [WorkdayEntityDialogComponent, CommonModule, ReactiveFormsModule, TranslateModule.forRoot()], providers: [ FormBuilder, DatePipe, @@ -218,21 +218,21 @@ describe('WorkdayEntityDialogComponent', () => { }); it('offers the dialog tour with the real audience flags, not hardcoded falses', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); localStorage.removeItem(TOUR_STORAGE_KEY); const tour = TestBed.inject(HelpTourService); - const start = jest.spyOn(tour, 'start').mockImplementation(() => undefined); + const start = vi.spyOn(tour, 'start').mockImplementation(() => undefined); component.isAdmin = true; component.isFirstUser = true; (component as any).startDialogTourOnce(); - jest.runAllTimers(); + vi.runAllTimers(); // Both axes, so a later entry gated on either is judged by the right one. expect(start).toHaveBeenCalledWith('dialog', { isAdmin: true, isFirstUser: true }); start.mockRestore(); - jest.useRealTimers(); + vi.useRealTimers(); }); }); @@ -953,8 +953,8 @@ describe('WorkdayEntityDialogComponent', () => { // Timing out aborts the request from this end, but the server may commit a // moment later, so the day's state is UNKNOWN rather than unchanged. const tour = TestBed.inject(HelpTourService); - const start = jest.spyOn(tour, 'start').mockImplementation(() => undefined); - jest.useFakeTimers(); + const start = vi.spyOn(tour, 'start').mockImplementation(() => undefined); + vi.useFakeTimers(); const put = new Subject(); mockPlanningsService.reconcileDay.mockReturnValue(put.asObservable()); armReconcile(); @@ -963,7 +963,7 @@ describe('WorkdayEntityDialogComponent', () => { expect(component.lockRequestInFlight).toBe(true); expect(mockDialogRef.disableClose).toBe(true); - jest.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); + vi.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); // Settled and usable again... expect(component.lockRequestInFlight).toBe(false); @@ -984,7 +984,7 @@ describe('WorkdayEntityDialogComponent', () => { expect(component.lockStateChanged).toBe(true); expect(mockDialogRef.close).not.toHaveBeenCalled(); - jest.useRealTimers(); + vi.useRealTimers(); start.mockRestore(); }); @@ -1046,7 +1046,7 @@ describe('WorkdayEntityDialogComponent', () => { armUnlock(); component.unlockWordCtrl.setValue('LÅS OP'); let disableCloseAtClose: boolean | undefined; - (mockDialogRef.close as jest.Mock) + (mockDialogRef.close as Mock) .mockImplementation(() => { disableCloseAtClose = mockDialogRef.disableClose; }); component.onUnlockConfirm(); @@ -1215,7 +1215,7 @@ describe('WorkdayEntityDialogComponent', () => { // so the structural invariants of the footer are read off the file. The // Playwright specs in shard `s` exercise the rendered version. const template = readFileSync( - join(__dirname, 'workday-entity-dialog.component.html'), 'utf8'); + join(MODULE_ROOT, 'components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.html'), 'utf8'); const buttonWithId = (id: string): string => { const match = new RegExp(`]*\\bid="${id}"[^>]*>`).exec(template); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.ts index be3b444c1..6d2342fae 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-planning-actions/workday-entity/workday-entity-dialog.component.ts @@ -2,11 +2,11 @@ import {Component, ElementRef, OnInit, TemplateRef, ViewChild, inject, OnDestroy } from '@angular/core'; import {MAT_DIALOG_DATA, MatDialog} from '@angular/material/dialog'; -import {TranslateService} from '@ngx-translate/core'; -import {DatePipe} from '@angular/common'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; +import {DatePipe, NgIf, NgFor, NgTemplateOutlet} from '@angular/common'; import {TimePlanningMessagesEnum} from '../../../../enums'; import {AssignedSiteModel, PlanningPrDayModel, GpsCoordinateModel, PictureSnapshotModel} from '../../../../models'; -import {MtxGridColumn} from '@ng-matero/extensions/grid'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; import {TimePlanningPnPlanningsService, TimePlanningPnGpsCoordinatesService, TimePlanningPnPictureSnapshotsService} from '../../../../services'; import {VersionHistoryModalComponent} from '../version-history-modal/version-history-modal.component'; import {Store} from '@ngrx/store'; @@ -17,7 +17,7 @@ import {TemplateFilesService} from 'src/app/common/services'; import {OperationResult, SharedTagModel} from 'src/app/common/models'; import {Observable, Subscription} from 'rxjs'; import {ToastrService} from 'ngx-toastr'; -import { MatDialogRef } from '@angular/material/dialog'; +import {MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; import {HelpEntryId} from '../../../../help/help.model'; import {HelpPanelService} from '../../../../help/services/help-panel.service'; import {HelpTourService} from '../../../../help/services/help-tour.service'; @@ -34,12 +34,24 @@ import { ReactiveFormsModule, FormArray, } from '@angular/forms'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatCheckbox} from '@angular/material/checkbox'; +import {CdkScrollable} from '@angular/cdk/scrolling'; +import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; +import {EformTagComponent} from 'src/app/common/modules/eform-shared/components'; +import {HelpHintComponent} from '../../../../help/components/help-hint/help-hint.component'; +import {HelpTourComponent} from '../../../../help/components/help-tour/help-tour.component'; +import {HelpIconComponent} from '../../../../help/components/help-icon/help-icon.component'; @Component({ selector: 'app-workday-entity-dialog', + imports: [NgIf, NgFor, NgTemplateOutlet, ReactiveFormsModule, TranslatePipe, MatIcon, MatIconButton, MatFormField, MatLabel, MatError, MatInput, MtxGrid, MatTooltip, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatCheckbox, CdkScrollable, NgxMaterialTimepickerModule, EformTagComponent, HelpHintComponent, HelpTourComponent, HelpIconComponent], templateUrl: './workday-entity-dialog.component.html', styleUrls: ['./workday-entity-dialog.component.scss'], - standalone: false }) export class WorkdayEntityDialogComponent implements OnInit, OnDestroy { private fb = inject(FormBuilder); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.spec.ts index 695fb0aa9..7b53aa13b 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.spec.ts @@ -1,5 +1,9 @@ +import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest'; +import type {Mocked, MockInstance} from 'vitest'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { MODULE_ROOT } from '../../../testing/source-paths'; +import { pageProviders } from '../../../testing/page-providers'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimePlanningsContainerComponent } from './time-plannings-container.component'; import { TimePlanningPnPlanningsService } from '../../../services/time-planning-pn-plannings.service'; @@ -9,7 +13,6 @@ import { Store } from '@ngrx/store'; import { selectCurrentUserIsFirstUser } from 'src/app/state'; import { BehaviorSubject, EMPTY, of, Subject, throwError } from 'rxjs'; import { addDays, endOfWeek, format, startOfWeek, subDays } from 'date-fns'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { ToastrService } from 'ngx-toastr'; import { HelpTourService } from '../../../help/services/help-tour.service'; @@ -19,30 +22,30 @@ import { LOCK_REQUEST_TIMEOUT_MS } from '../day-lock.util'; describe('TimePlanningsContainerComponent', () => { let component: TimePlanningsContainerComponent; let fixture: ComponentFixture; - let mockPlanningsService: jest.Mocked; - let mockSettingsService: jest.Mocked; - let mockDialog: jest.Mocked; - let mockStore: jest.Mocked; + let mockPlanningsService: Mocked; + let mockSettingsService: Mocked; + let mockDialog: Mocked; + let mockStore: Mocked; beforeEach(async () => { mockPlanningsService = { - getPlannings: jest.fn(), - updatePlanning: jest.fn(), - reconcileThrough: jest.fn(), + getPlannings: vi.fn(), + updatePlanning: vi.fn(), + reconcileThrough: vi.fn(), } as any; mockSettingsService = { - getAvailableSites: jest.fn(), - getResignedSites: jest.fn(), - getAssignedSite: jest.fn(), - updateAssignedSite: jest.fn(), - getAvailableTags: jest.fn(), - getPayrollSettings: jest.fn(), + getAvailableSites: vi.fn(), + getResignedSites: vi.fn(), + getAssignedSite: vi.fn(), + updateAssignedSite: vi.fn(), + getAvailableTags: vi.fn(), + getPayrollSettings: vi.fn(), } as any; mockDialog = { - open: jest.fn(), + open: vi.fn(), } as any; mockStore = { - select: jest.fn(), + select: vi.fn(), } as any; mockStore.select.mockReturnValue(of('en-US')); @@ -52,17 +55,16 @@ describe('TimePlanningsContainerComponent', () => { mockPlanningsService.getPlannings.mockReturnValue(of({ success: true, model: [] }) as any); await TestBed.configureTestingModule({ - declarations: [TimePlanningsContainerComponent], - imports: [TranslateModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [TimePlanningsContainerComponent, TranslateModule.forRoot()], providers: [ + ...pageProviders(), { provide: TimePlanningPnPlanningsService, useValue: mockPlanningsService }, { provide: TimePlanningPnSettingsService, useValue: mockSettingsService }, { provide: MatDialog, useValue: mockDialog }, { provide: Store, useValue: mockStore }, { provide: ToastrService, - useValue: { success: jest.fn(), warning: jest.fn(), error: jest.fn() }, + useValue: { success: vi.fn(), warning: vi.fn(), error: vi.fn() }, }, ] }).compileComponents(); @@ -142,7 +144,7 @@ describe('TimePlanningsContainerComponent', () => { describe('Event Handlers', () => { it('should call getPlannings when onTimePlanningChanged is triggered', () => { - jest.spyOn(component, 'getPlannings'); + vi.spyOn(component, 'getPlannings'); component.onTimePlanningChanged({}); @@ -150,7 +152,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('should call getPlannings when onAssignedSiteChanged is triggered', () => { - jest.spyOn(component, 'getPlannings'); + vi.spyOn(component, 'getPlannings'); component.onAssignedSiteChanged({}); @@ -158,7 +160,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('should update siteId and call getPlannings when onSiteChanged is triggered', () => { - jest.spyOn(component, 'getPlannings'); + vi.spyOn(component, 'getPlannings'); const testSiteId = 123; component.onSiteChanged(testSiteId); @@ -227,7 +229,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('should update selectedTagIds and call getPlannings when onTagsChanged is triggered', () => { - jest.spyOn(component, 'getPlannings'); + vi.spyOn(component, 'getPlannings'); const testTagIds = [1, 2, 3]; component.onTagsChanged(testTagIds); @@ -237,7 +239,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('should hold an empty list, never null, when the select clears its last tag', () => { - jest.spyOn(component, 'getPlannings'); + vi.spyOn(component, 'getPlannings'); component.selectedTagIds = [1]; component.onTagsChanged(null); @@ -284,19 +286,19 @@ describe('TimePlanningsContainerComponent', () => { describe('Page help tour', () => { let tour: HelpTourService; - let start: jest.SpyInstance; + let start: MockInstance; beforeEach(() => { localStorage.clear(); tour = TestBed.inject(HelpTourService); - start = jest.spyOn(tour, 'start').mockImplementation(() => undefined); - jest.useFakeTimers(); + start = vi.spyOn(tour, 'start').mockImplementation(() => undefined); + vi.useFakeTimers(); component.dateFrom = new Date(2024, 0, 15); component.dateTo = new Date(2024, 0, 21); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); start.mockRestore(); localStorage.clear(); }); @@ -308,7 +310,7 @@ describe('TimePlanningsContainerComponent', () => { mockPlanningsService.getPlannings.mockReturnValue(of({ success: true, model: [] }) as any); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).not.toHaveBeenCalled(); }); @@ -318,7 +320,7 @@ describe('TimePlanningsContainerComponent', () => { of({ success: true, model: [{ siteId: 1, siteName: 'A' }] }) as any); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); // Literals, not component.isAdmin / component.isFirstUser: reading the expected // values off the component under test asserts nothing about what was passed. @@ -330,17 +332,17 @@ describe('TimePlanningsContainerComponent', () => { // around a start the gate refuses would mean this planner never gets the // tour on this page visit, even once help becomes visible to them. const visibility = TestBed.inject(HelpVisibilityService); - const isVisible = jest.spyOn(visibility, 'isVisible', 'get').mockReturnValue(false); + const isVisible = vi.spyOn(visibility, 'isVisible', 'get').mockReturnValue(false); mockPlanningsService.getPlannings.mockReturnValue( of({ success: true, model: [{ siteId: 1, siteName: 'A' }] }) as any); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).not.toHaveBeenCalled(); isVisible.mockReturnValue(true); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).toHaveBeenCalledWith('page', { isAdmin: false, isFirstUser: true }); isVisible.mockRestore(); @@ -351,9 +353,9 @@ describe('TimePlanningsContainerComponent', () => { of({ success: true, model: [{ siteId: 1, siteName: 'A' }] }) as any); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).toHaveBeenCalledTimes(1); }); @@ -362,7 +364,7 @@ describe('TimePlanningsContainerComponent', () => { // start('dialog') is otherwise called from one place, gated on hasSeen, so // this is the only route back to the dialog tour once it has been skipped. component.replayTour('dialog'); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).toHaveBeenCalledWith('dialog', { isAdmin: false, isFirstUser: true }); }); @@ -373,7 +375,7 @@ describe('TimePlanningsContainerComponent', () => { of({ success: true, model: [{ siteId: 1, siteName: 'A' }] }) as any); component.getPlannings(); - jest.runAllTimers(); + vi.runAllTimers(); expect(start).not.toHaveBeenCalled(); }); @@ -629,7 +631,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('reports the two skip reasons as separate figures, never merged', () => { - const instant = jest.spyOn(TestBed.inject(TranslateService), 'instant'); + const instant = vi.spyOn(TestBed.inject(TranslateService), 'instant'); mockPlanningsService.reconcileThrough.mockReturnValue(of({ success: true, model: resultModel({ @@ -653,7 +655,7 @@ describe('TimePlanningsContainerComponent', () => { }); it('leaves the no-registration figure out when there is none', () => { - const instant = jest.spyOn(TestBed.inject(TranslateService), 'instant'); + const instant = vi.spyOn(TestBed.inject(TranslateService), 'instant'); mockPlanningsService.reconcileThrough.mockReturnValue( of({ success: true, model: resultModel() }) as any); @@ -751,15 +753,15 @@ describe('TimePlanningsContainerComponent', () => { }); it('stops waiting for a request that never answers, and reloads rather than claim failure', () => { - jest.useFakeTimers(); - const tour = jest.spyOn(TestBed.inject(HelpTourService), 'start').mockImplementation(() => undefined); + vi.useFakeTimers(); + const tour = vi.spyOn(TestBed.inject(HelpTourService), 'start').mockImplementation(() => undefined); const request = new Subject(); mockPlanningsService.reconcileThrough.mockReturnValue(request as any); component.confirmReconcileThrough(); expect(component.reconcileInFlight).toBe(true); - jest.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); + vi.advanceTimersByTime(LOCK_REQUEST_TIMEOUT_MS + 1); expect(component.reconcileInFlight).toBe(false); expect(toastr.error).toHaveBeenCalledWith('lockRequestUncertain'); @@ -774,7 +776,7 @@ describe('TimePlanningsContainerComponent', () => { expect(mockPlanningsService.getPlannings.mock.calls.length).toBe(after); tour.mockRestore(); - jest.useRealTimers(); + vi.useRealTimers(); }); it('refuses to commit twice while a request is in flight', () => { @@ -885,7 +887,7 @@ describe('TimePlanningsContainerComponent', () => { // what some other user may do. Read for the RIGHT flag, too: isAdmin still gates // the payroll export button in this same toolbar. const template = readFileSync( - join(__dirname, 'time-plannings-container.component.html'), 'utf8'); + join(MODULE_ROOT, 'components/plannings/time-plannings-container/time-plannings-container.component.html'), 'utf8'); const field = /]*data-tp-help="toolbar\.reconcileThrough"[^>]*>/.exec(template); expect(field).not.toBeNull(); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.ts index aa0651c2d..53cc0f44f 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-container/time-plannings-container.component.ts @@ -13,7 +13,7 @@ import { } from '../../../services'; import {startOfWeek, endOfWeek, format, startOfDay, subDays} from 'date-fns'; import {ToastrService} from 'ngx-toastr'; -import {TranslateService} from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { assertLockOutcomeHandled, buildReconcilePreview, ReconcilePreview, sendLockRequest, } from '../day-lock.util'; @@ -24,19 +24,32 @@ import {MatDialog} from '@angular/material/dialog'; import { DownloadExcelDialogComponent, DownloadExcelDialogData, PayrollExportDialogComponent } from 'src/app/plugins/modules/time-planning-pn/components'; -import {MatDatepickerInputEvent} from '@angular/material/datepicker'; +import {MatDatepickerInputEvent, MatDatepicker, MatDatepickerInput, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate} from '@angular/material/datepicker'; import {HelpAudience, HelpEntryId, HelpTourName, HelpUiStrings} from '../../../help/help.model'; import {HelpContentService} from '../../../help/services/help-content.service'; import {HelpPanelService} from '../../../help/services/help-panel.service'; import {HelpTourService} from '../../../help/services/help-tour.service'; import {HelpVisibilityService} from '../../../help/services/help-visibility.service'; +import {NgIf, AsyncPipe} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {MatIcon} from '@angular/material/icon'; +import {MatFormField, MatLabel, MatPrefix} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatSlideToggle} from '@angular/material/slide-toggle'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; +import {HelpPanelComponent} from '../../../help/components/help-panel/help-panel.component'; +import {HelpTourComponent} from '../../../help/components/help-tour/help-tour.component'; +import {HelpIconComponent} from '../../../help/components/help-icon/help-icon.component'; +import {TimePlanningsTableComponent} from '../time-plannings-table/time-plannings-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-time-plannings-container', + imports: [NgIf, AsyncPipe, FormsModule, TranslatePipe, MatIcon, MatFormField, MatLabel, MatPrefix, MatInput, MtxSelect, MatTooltip, MatDatepicker, MatDatepickerInput, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate, MatSlideToggle, EformNewSubheaderComponent, HelpPanelComponent, HelpTourComponent, HelpIconComponent, TimePlanningsTableComponent], templateUrl: './time-plannings-container.component.html', styleUrls: ['./time-plannings-container.component.scss'], - standalone: false }) export class TimePlanningsContainerComponent implements OnInit, OnDestroy { private store = inject(Store); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts index ba5907ac2..d2ecf3cf9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.spec.ts @@ -1,5 +1,8 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked, Mock} from 'vitest'; import { readFileSync } from 'fs'; import { join } from 'path'; +import { MODULE_ROOT } from '../../../testing/source-paths'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimePlanningsTableComponent } from './time-plannings-table.component'; import { TimePlanningPnPlanningsService } from '../../../services/time-planning-pn-plannings.service'; @@ -7,7 +10,7 @@ import { TimePlanningPnSettingsService } from '../../../services/time-planning-p import { MatDialog } from '@angular/material/dialog'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; -import { ChangeDetectorRef, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectorRef } from '@angular/core'; import { Store } from '@ngrx/store'; import { selectCurrentUserIsFirstUser } from 'src/app/state'; import { BehaviorSubject, of } from 'rxjs'; @@ -22,31 +25,31 @@ registerTestLocales(); describe('TimePlanningsTableComponent', () => { let component: TimePlanningsTableComponent; let fixture: ComponentFixture; - let mockPlanningsService: jest.Mocked; - let mockSettingsService: jest.Mocked; - let mockDialog: jest.Mocked; - let mockTranslateService: jest.Mocked; - let mockStore: jest.Mocked; + let mockPlanningsService: Mocked; + let mockSettingsService: Mocked; + let mockDialog: Mocked; + let mockTranslateService: Mocked; + let mockStore: Mocked; beforeEach(async () => { mockPlanningsService = { - getPlannings: jest.fn(), - updatePlanning: jest.fn(), + getPlannings: vi.fn(), + updatePlanning: vi.fn(), } as any; mockSettingsService = { - getAssignedSite: jest.fn(), - updateAssignedSite: jest.fn(), + getAssignedSite: vi.fn(), + updateAssignedSite: vi.fn(), } as any; mockDialog = { - open: jest.fn(), + open: vi.fn(), } as any; mockTranslateService = { - stream: jest.fn(), - instant: jest.fn(), + stream: vi.fn(), + instant: vi.fn(), onLangChange: of({ lang: 'en' }), } as any; mockStore = { - select: jest.fn(), + select: vi.fn(), } as any; mockStore.select.mockReturnValue(of(true)); @@ -54,9 +57,7 @@ describe('TimePlanningsTableComponent', () => { mockTranslateService.instant.mockReturnValue('Translated'); await TestBed.configureTestingModule({ - declarations: [TimePlanningsTableComponent], - imports: [TranslateModule.forRoot()], - schemas: [NO_ERRORS_SCHEMA], + imports: [TimePlanningsTableComponent, TranslateModule.forRoot()], providers: [ { provide: TimePlanningPnPlanningsService, useValue: mockPlanningsService }, { provide: TimePlanningPnSettingsService, useValue: mockSettingsService }, @@ -410,11 +411,11 @@ describe('TimePlanningsTableComponent', () => { } }); - const dialogData = () => (mockDialog.open as jest.Mock).mock.calls[0][1].data; + const dialogData = () => (mockDialog.open as Mock).mock.calls[0][1].data; beforeEach(() => { mockSettingsService.getAssignedSite.mockReturnValue(of({ success: true, model: {} }) as any); - (mockDialog.open as jest.Mock).mockReturnValue({ afterClosed: () => of(undefined) } as any); + (mockDialog.open as Mock).mockReturnValue({ afterClosed: () => of(undefined) } as any); }); it('does not open a placeholder day, which has no registration behind it', () => { @@ -459,14 +460,14 @@ describe('TimePlanningsTableComponent', () => { describe('the close contract after a reconcile or unlock', () => { /** A dialog that closed with `payload`, having reported `lockStateChanged`. */ const dialogThatClosed = (lockStateChanged: boolean, payload: any) => { - (mockDialog.open as jest.Mock).mockReturnValue({ + (mockDialog.open as Mock).mockReturnValue({ componentInstance: { lockStateChanged }, afterClosed: () => of(payload), } as any); }; it('reloads the grid and sends no save when the dialog reconciled the day', () => { - const changed = jest.fn(); + const changed = vi.fn(); component.timePlanningChanged.subscribe(changed); // A reconcile can close through Cancel, Esc or the backdrop, so the close // payload proves nothing either way; lockStateChanged is what decides. @@ -546,7 +547,7 @@ describe('TimePlanningsTableComponent', () => { it('should format time correctly when on same day', () => { // This test depends on the DatePipe transform which we've mocked - jest.spyOn(component['datePipe'], 'transform').mockReturnValue('10:30'); + vi.spyOn(component['datePipe'], 'transform').mockReturnValue('10:30'); const result = component.getStopTimeDisplay('2024-01-15T08:00:00', '2024-01-15T10:30:00'); expect(result).toBe('10:30'); }); @@ -564,7 +565,7 @@ describe('TimePlanningsTableComponent', () => { }); it("uses HH:mm format when row.useOneMinuteIntervals is false", () => { - const transformSpy = jest + const transformSpy = vi .spyOn(component['datePipe'], 'transform') .mockReturnValue('07:03'); const result = component.formatStamp( @@ -576,7 +577,7 @@ describe('TimePlanningsTableComponent', () => { }); it("uses HH:mm format when row.useOneMinuteIntervals is true", () => { - const transformSpy = jest + const transformSpy = vi .spyOn(component['datePipe'], 'transform') .mockReturnValue('07:03'); const result = component.formatStamp( @@ -588,7 +589,7 @@ describe('TimePlanningsTableComponent', () => { }); it("falls back to HH:mm when row is null/undefined (defensive)", () => { - const transformSpy = jest + const transformSpy = vi .spyOn(component['datePipe'], 'transform') .mockReturnValue('07:03'); const result = component.formatStamp(null as any, '2026-05-15T07:03:53Z'); @@ -618,7 +619,7 @@ describe('TimePlanningsTableComponent', () => { }); it("uses HH:mm format when flag off (legacy parity)", () => { - const transformSpy = jest + const transformSpy = vi .spyOn(component['datePipe'], 'transform') .mockReturnValue('15:30'); const result = component.getStopTimeDisplayWithSeconds( @@ -631,7 +632,7 @@ describe('TimePlanningsTableComponent', () => { }); it("uses HH:mm format when flag on", () => { - const transformSpy = jest + const transformSpy = vi .spyOn(component['datePipe'], 'transform') .mockReturnValue('15:30'); const result = component.getStopTimeDisplayWithSeconds( @@ -646,7 +647,7 @@ describe('TimePlanningsTableComponent', () => { describe('formatDuration', () => { beforeEach(() => { - jest + vi .spyOn(component['translateService'], 'instant') .mockImplementation((key: string) => key); }); @@ -709,7 +710,7 @@ describe('TimePlanningsTableComponent', () => { * view. */ const stubHeaderFlush = (target: any) => { - target.cdr = { detectChanges: jest.fn() }; + target.cdr = { detectChanges: vi.fn() }; }; /** @@ -734,8 +735,8 @@ describe('TimePlanningsTableComponent', () => { * would silently widen a previewed scope. */ describe('row selection', () => { - const changed = jest.fn(); - const reset = jest.fn(); + const changed = vi.fn(); + const reset = vi.fn(); beforeEach(() => { changed.mockClear(); @@ -927,7 +928,7 @@ describe('TimePlanningsTableComponent', () => { // binding is read off the file — and read for the RIGHT flag: isAdmin still // gates unrelated chrome in this same template. const template = readFileSync( - join(__dirname, 'time-plannings-table.component.html'), 'utf8'); + join(MODULE_ROOT, 'components/plannings/time-plannings-table/time-plannings-table.component.html'), 'utf8'); expect(template).toContain('[rowSelectable]="isFirstUser"'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts index c488441ba..1a0d91013 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/plannings/time-plannings-table/time-plannings-table.component.ts @@ -5,12 +5,12 @@ import { } from '@angular/core'; import {Subscription, take} from 'rxjs'; import {AssignedSiteModel, TimePlanningModel} from '../../../models'; -import {MtxGridColumn} from '@ng-matero/extensions/grid'; -import {TranslateService} from '@ngx-translate/core'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import {TimePlanningPnPlanningsService, TimePlanningPnSettingsService} from '../../../services'; import {MatDialog} from '@angular/material/dialog'; import {AssignedSiteDialogComponent, WorkdayEntityDialogComponent} from '../'; -import {DatePipe} from '@angular/common'; +import {DatePipe, NgIf, AsyncPipe} from '@angular/common'; import * as R from 'ramda'; import {TimePlanningMessagesEnum} from '../../../enums'; import {Store} from '@ngrx/store'; @@ -21,13 +21,18 @@ import {HelpPanelService} from '../../../help/services/help-panel.service'; import { dayAt, dayKey, dayLockState, formatReconciledProvenance, isDayLocked, isDaySealed, ReconcilePreview, } from '../day-lock.util'; +import {MatIcon} from '@angular/material/icon'; +import {MatTooltip} from '@angular/material/tooltip'; +import {EformTagComponent} from 'src/app/common/modules/eform-shared/components'; +import {HelpHintComponent} from '../../../help/components/help-hint/help-hint.component'; +import {HelpIconComponent} from '../../../help/components/help-icon/help-icon.component'; @Component({ selector: 'app-time-plannings-table', + imports: [NgIf, AsyncPipe, TranslatePipe, MatIcon, MtxGrid, MatTooltip, EformTagComponent, HelpHintComponent, HelpIconComponent], templateUrl: './time-plannings-table.component.html', styleUrls: ['./time-plannings-table.component.scss'], encapsulation: ViewEncapsulation.None, - standalone: false }) export class TimePlanningsTableComponent implements OnInit, OnChanges, OnDestroy, AfterViewChecked { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/components/settings/time-planning-settings/time-planning-settings.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/components/settings/time-planning-settings/time-planning-settings.component.ts index 35b9e3a7c..f8d8b4f13 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/components/settings/time-planning-settings/time-planning-settings.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/components/settings/time-planning-settings/time-planning-settings.component.ts @@ -6,12 +6,23 @@ import {TimePlanningPnSettingsService} from '../../../services'; import {TimePlanningSettingsModel} from '../../../models'; import {selectCurrentUserIsFirstUser} from 'src/app/state'; import {Store} from '@ngrx/store'; +import {NgIf, NgFor} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatButton} from '@angular/material/button'; +import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatSlideToggle} from '@angular/material/slide-toggle'; +import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; @Component({ selector: 'app-time-planning-settings', + imports: [NgIf, NgFor, FormsModule, TranslatePipe, MatButton, MatCard, MatCardContent, MatCardHeader, MatFormField, MatLabel, MatInput, MtxSelect, MatSlideToggle, NgxMaterialTimepickerModule, EformNewSubheaderComponent], templateUrl: './time-planning-settings.component.html', styleUrls: ['./time-planning-settings.component.scss'], - standalone: false }) export class TimePlanningSettingsComponent implements OnInit, OnDestroy { private timePlanningPnSettingsService = inject(TimePlanningPnSettingsService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.spec.ts index 6ca17a952..c6a6d1d56 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.spec.ts @@ -1,6 +1,8 @@ +import {describe, it, expect, beforeEach} from 'vitest'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIconModule } from '@angular/material/icon'; import { TranslateService } from '@ngx-translate/core'; +import { detectChanges } from '../../../testing/detect-changes'; import { HelpHintComponent } from './help-hint.component'; import { of } from 'rxjs'; import { enUS } from '../../i18n/enUS'; @@ -23,8 +25,7 @@ describe('HelpHintComponent', () => { // The stub is shared by every case here; the gate tests flip it. helpVisibility.isVisible = true; await TestBed.configureTestingModule({ - declarations: [HelpHintComponent], - imports: [MatIconModule], + imports: [HelpHintComponent, MatIconModule], providers: [ { provide: TranslateService, useValue: { currentLang: 'en-US' } }, provideHelpVisibility, @@ -35,46 +36,46 @@ describe('HelpHintComponent', () => { it('renders the entry short text', () => { fixture.componentInstance.helpId = 'dayCell.futureDisabled'; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.textContent).toContain(enUS['dayCell.futureDisabled'].short); }); it('uses the info tone by default and warn when asked', () => { fixture.componentInstance.helpId = 'dayCell.futureDisabled'; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.help-text')?.classList).not.toContain('help-text--warn'); fixture.componentInstance.tone = 'warn'; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.help-text')?.classList).toContain('help-text--warn'); }); it('renders nothing for an unknown id', () => { fixture.componentInstance.helpId = 'nope' as never; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.help-text')).toBeNull(); }); it('renders the correct icon based on tone', () => { fixture.componentInstance.helpId = 'dayCell.futureDisabled'; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.mat-icon').textContent).toContain('info'); fixture.componentInstance.tone = 'warn'; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.mat-icon').textContent).toContain('warning'); }); it('renders nothing when help is not visible, and the hint again when it is', () => { fixture.componentInstance.helpId = 'dayCell.futureDisabled'; helpVisibility.isVisible = false; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.help-text')).toBeNull(); // The admin half: otherwise the assertion above is indistinguishable from // the component rendering nothing under any circumstances. helpVisibility.isVisible = true; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('.help-text')).not.toBeNull(); expect(fixture.nativeElement.textContent).toContain(enUS['dayCell.futureDisabled'].short); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.ts index 10afb835b..4e538e885 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-hint/help-hint.component.ts @@ -1,11 +1,13 @@ import { Component, Input } from '@angular/core'; import { HelpEntryChromeBase } from '../help-chrome.base'; +import {NgIf} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; @Component({ selector: 'tp-help-hint', + imports: [NgIf, MatIcon], templateUrl: './help-hint.component.html', styleUrls: ['./help-hint.component.scss'], - standalone: false, }) export class HelpHintComponent extends HelpEntryChromeBase { @Input() tone: 'info' | 'warn' = 'info'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.spec.ts index 1e320825d..46cb64c3a 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach} from 'vitest'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CloseScrollStrategy, OverlayModule } from '@angular/cdk/overlay'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -8,6 +9,8 @@ import { HelpIconComponent } from './help-icon.component'; import { of } from 'rxjs'; import { enUS } from '../../i18n/enUS'; import { HelpVisibilityService } from '../../services/help-visibility.service'; +import { detectChanges } from '../../../testing/detect-changes'; +import { useWorkingMutationObserver } from '../../../testing/mutation-observer'; /** * The one dependency the help chrome gained when help became admin-only. A stub @@ -20,14 +23,15 @@ const provideHelpVisibility = { provide: HelpVisibilityService, useValue: helpVi describe('HelpIconComponent', () => { + useWorkingMutationObserver(); + let fixture: ComponentFixture; beforeEach(async () => { // The stub is shared by every case here; the gate tests flip it. helpVisibility.isVisible = true; await TestBed.configureTestingModule({ - declarations: [HelpIconComponent], - imports: [OverlayModule, NoopAnimationsModule, MatIconModule, MatButtonModule], + imports: [HelpIconComponent, OverlayModule, NoopAnimationsModule, MatIconModule, MatButtonModule], providers: [ { provide: TranslateService, useValue: { currentLang: 'en-US' } }, provideHelpVisibility, @@ -36,7 +40,7 @@ describe('HelpIconComponent', () => { fixture = TestBed.createComponent(HelpIconComponent); fixture.componentInstance.helpId = 'toolbar.dateRange'; - fixture.detectChanges(); + detectChanges(fixture); }); it('labels the button with the entry title', () => { @@ -47,7 +51,7 @@ describe('HelpIconComponent', () => { it('starts closed and opens on click', () => { expect(fixture.componentInstance.isOpen).toBe(false); fixture.nativeElement.querySelector('button').click(); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.isOpen).toBe(true); }); @@ -62,11 +66,11 @@ describe('HelpIconComponent', () => { it('closes on Escape', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); expect(document.querySelector('.tp-help-popover')).not.toBeNull(); press('Escape'); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.isOpen).toBe(false); expect(document.querySelector('.tp-help-popover')).toBeNull(); @@ -74,10 +78,10 @@ describe('HelpIconComponent', () => { it('ignores other keys', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); press('a'); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.isOpen).toBe(true); expect(document.querySelector('.tp-help-popover')).not.toBeNull(); @@ -87,12 +91,12 @@ describe('HelpIconComponent', () => { const seen: string[] = []; fixture.componentInstance.openInPanel.subscribe(id => seen.push(id)); fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); const more = document.querySelector('.tp-help-popover__more') as HTMLButtonElement; expect(more).not.toBeNull(); more.click(); - fixture.detectChanges(); + detectChanges(fixture); expect(seen).toEqual(['toolbar.dateRange']); expect(fixture.componentInstance.isOpen).toBe(false); @@ -100,7 +104,7 @@ describe('HelpIconComponent', () => { it('closes when a click lands outside the popover, without a backdrop to swallow it', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); // No backdrop means the same click that dismisses the popover reaches the // control underneath - on this page every day cell is a click target. @@ -109,7 +113,7 @@ describe('HelpIconComponent', () => { const outside = document.createElement('button'); document.body.appendChild(outside); outside.dispatchEvent(new MouseEvent('click', { bubbles: true })); - fixture.detectChanges(); + detectChanges(fixture); outside.remove(); expect(fixture.componentInstance.isOpen).toBe(false); @@ -117,18 +121,18 @@ describe('HelpIconComponent', () => { it('stays open when the click lands inside the popover', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); const body = document.querySelector('.tp-help-popover__body') as HTMLElement; body.dispatchEvent(new MouseEvent('click', { bubbles: true })); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.isOpen).toBe(true); }); it('describes the popover as a note, not a modal dialog it does not implement', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); const popover = document.querySelector('.tp-help-popover'); expect(popover).not.toBeNull(); @@ -144,13 +148,13 @@ describe('HelpIconComponent', () => { it('dismisses on scroll', () => { fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); expect(document.querySelector('.tp-help-popover')).not.toBeNull(); // ScrollDispatcher listens for 'scroll' on the document and pushes through // its scrolled() stream, which the close strategy is subscribed to. document.dispatchEvent(new Event('scroll')); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.isOpen).toBe(false); expect(document.querySelector('.tp-help-popover')).toBeNull(); @@ -159,7 +163,7 @@ describe('HelpIconComponent', () => { it('renders nothing for an unknown id rather than throwing', () => { const other = TestBed.createComponent(HelpIconComponent); other.componentInstance.helpId = 'nope' as never; - expect(() => other.detectChanges()).not.toThrow(); + expect(() => detectChanges(other)).not.toThrow(); expect(other.nativeElement.querySelector('button')).toBeNull(); }); @@ -169,7 +173,7 @@ describe('HelpIconComponent', () => { // forced — no popover either. helpVisibility.isVisible = false; fixture.componentInstance.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('button')).toBeNull(); expect(document.querySelector('.tp-help-popover')).toBeNull(); @@ -177,7 +181,7 @@ describe('HelpIconComponent', () => { // And an admin still gets all of it. Without this half, the assertions above // would pass just as well if the component had been deleted. helpVisibility.isVisible = true; - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.nativeElement.querySelector('button')).not.toBeNull(); expect(document.querySelector('.tp-help-popover')).not.toBeNull(); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.ts index e53e74277..962c3a17b 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-icon/help-icon.component.ts @@ -1,13 +1,16 @@ import { Component, EventEmitter, Output } from '@angular/core'; -import { ConnectedPosition, Overlay, ScrollStrategy } from '@angular/cdk/overlay'; +import {ConnectedPosition, Overlay, ScrollStrategy, CdkConnectedOverlay, CdkOverlayOrigin} from '@angular/cdk/overlay'; import { HelpEntryId } from '../../help.model'; import { HelpEntryChromeBase } from '../help-chrome.base'; +import {NgIf} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; @Component({ selector: 'tp-help-icon', + imports: [NgIf, MatIcon, MatIconButton, CdkConnectedOverlay, CdkOverlayOrigin], templateUrl: './help-icon.component.html', styleUrls: ['./help-icon.component.scss'], - standalone: false, }) export class HelpIconComponent extends HelpEntryChromeBase { @Output() openInPanel = new EventEmitter(); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.spec.ts index 7e4b1405c..b26ffd3a5 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; import { SimpleChange } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; @@ -10,6 +11,7 @@ import { enUS, enUSUi } from '../../i18n/enUS'; import { HelpPanelService } from '../../services/help-panel.service'; import { HelpVisibilityService } from '../../services/help-visibility.service'; import { HelpPanelComponent } from './help-panel.component'; +import { detectChanges } from '../../../testing/detect-changes'; /** * The one dependency the help chrome gained when help became admin-only. A stub @@ -42,8 +44,7 @@ describe('HelpPanelComponent', () => { // The stub is shared by every case here; the gate tests flip it. helpVisibility.isVisible = true; await TestBed.configureTestingModule({ - declarations: [HelpPanelComponent], - imports: [FormsModule, MatIconModule, MatButtonModule], + imports: [HelpPanelComponent, FormsModule, MatIconModule, MatButtonModule], providers: [ HelpPanelService, { provide: TranslateService, useValue: { currentLang: 'en-US' } }, @@ -54,7 +55,7 @@ describe('HelpPanelComponent', () => { fixture = TestBed.createComponent(HelpPanelComponent); component = fixture.componentInstance; panel = TestBed.inject(HelpPanelService); - fixture.detectChanges(); + detectChanges(fixture); }); it('renders nothing while closed', () => { @@ -64,7 +65,7 @@ describe('HelpPanelComponent', () => { it('renders its chrome from the help ui strings, never a translate key', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); const header = fixture.nativeElement.querySelector('.tp-help-panel__top h5') as HTMLElement; const input = fixture.nativeElement.querySelector('.tp-help-panel__search input') as HTMLInputElement; @@ -80,7 +81,7 @@ describe('HelpPanelComponent', () => { it('browses grouped sections in order when open with no query', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(panelEl()).not.toBeNull(); expect(component.isSearching).toBe(false); @@ -100,7 +101,7 @@ describe('HelpPanelComponent', () => { it('groups every entry under its own section', () => { component.isAdmin = true; panel.open(); - fixture.detectChanges(); + detectChanges(fixture); for (const group of component.sections) { expect(group.entries.every(entry => entry.section === group.section)).toBe(true); @@ -110,10 +111,10 @@ describe('HelpPanelComponent', () => { it('switches to results when a query is typed and back when it is cleared', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); component.onQueryChange('vacation'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isSearching).toBe(true); expect(component.results.length).toBeGreaterThan(0); @@ -124,7 +125,7 @@ describe('HelpPanelComponent', () => { expect(countText()).not.toContain(enUSUi.noResults); component.clearQuery(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isSearching).toBe(false); expect(component.results).toEqual([]); @@ -133,12 +134,12 @@ describe('HelpPanelComponent', () => { it('drives the query from the search input through ngModel', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); const input = fixture.nativeElement.querySelector('.tp-help-panel__search input') as HTMLInputElement; input.value = 'vacation'; input.dispatchEvent(new Event('input')); - fixture.detectChanges(); + detectChanges(fixture); expect(component.query).toBe('vacation'); expect(component.isSearching).toBe(true); @@ -148,7 +149,7 @@ describe('HelpPanelComponent', () => { it('treats a whitespace-only query as browsing', () => { panel.open(); component.onQueryChange(' '); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isSearching).toBe(false); expect(sectionHeadings().length).toBe(5); @@ -157,13 +158,13 @@ describe('HelpPanelComponent', () => { it('hides admin-only entries when isAdmin is false, in browse and in results', () => { component.isAdmin = false; panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(browsedIds()).not.toContain('toolbar.payrollExport'); expect(text()).not.toContain(enUS['toolbar.payrollExport'].title); component.onQueryChange('payroll'); - fixture.detectChanges(); + detectChanges(fixture); expect(resultIds().length).toBeGreaterThan(0); expect(resultIds()).not.toContain('toolbar.payrollExport'); @@ -173,7 +174,7 @@ describe('HelpPanelComponent', () => { component.isAdmin = true; panel.open(); component.onQueryChange('payroll'); - fixture.detectChanges(); + detectChanges(fixture); expect(resultIds()).toContain('toolbar.payrollExport'); expect(text()).toContain(enUS['toolbar.payrollExport'].title); @@ -181,7 +182,7 @@ describe('HelpPanelComponent', () => { it('marks and expands the deep-link target', () => { panel.open('flex.sumFlex'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.targetId).toBe('flex.sumFlex'); expect(component.expanded).toBe('flex.sumFlex'); @@ -194,19 +195,19 @@ describe('HelpPanelComponent', () => { it('expands and collapses an entry on click', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(text()).not.toContain(enUS['task.registerVacation'].steps?.[0] as string); component.toggleEntry('task.registerVacation'); - fixture.detectChanges(); + detectChanges(fixture); const steps = Array.from(fixture.nativeElement.querySelectorAll('.tp-help-entry__steps li')) .map(el => ((el as HTMLElement).textContent ?? '').trim()); expect(steps).toEqual(enUS['task.registerVacation'].steps); component.toggleEntry('task.registerVacation'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.expanded).toBeNull(); expect(fixture.nativeElement.querySelector('.tp-help-entry__steps')).toBeNull(); @@ -215,17 +216,17 @@ describe('HelpPanelComponent', () => { it('closes through the service and forgets the query', () => { panel.open(); component.onQueryChange('vacation'); - fixture.detectChanges(); + detectChanges(fixture); component.close(); - fixture.detectChanges(); + detectChanges(fixture); expect(panelEl()).toBeNull(); expect(component.query).toBe(''); expect(component.results).toEqual([]); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isSearching).toBe(false); expect(sectionHeadings().length).toBe(5); @@ -238,10 +239,10 @@ describe('HelpPanelComponent', () => { const replays: HelpTourName[] = []; component.replayTourRequested.subscribe(tour => replays.push(tour)); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); clickReplay(); - fixture.detectChanges(); + detectChanges(fixture); expect(replays).toEqual(['page']); expect(panelEl()).toBeNull(); @@ -254,23 +255,23 @@ describe('HelpPanelComponent', () => { const replays: HelpTourName[] = []; component.replayTourRequested.subscribe(tour => replays.push(tour)); panel.open('dayCell.save', 'dialog'); - fixture.detectChanges(); + detectChanges(fixture); clickReplay(); - fixture.detectChanges(); + detectChanges(fixture); expect(replays).toEqual(['dialog']); }); it('goes back to the page tour once the panel has closed', () => { panel.open('dayCell.save', 'dialog'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.surface).toBe('dialog'); panel.close(); - fixture.detectChanges(); + detectChanges(fixture); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.surface).toBe('page'); }); @@ -279,7 +280,7 @@ describe('HelpPanelComponent', () => { panel.open(); // 'avatar' appears in exactly one entry's prose. component.onQueryChange('avatar'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.results.length).toBe(1); expect(enUSUi.resultCountOne).not.toBe(enUSUi.resultCount); @@ -290,9 +291,9 @@ describe('HelpPanelComponent', () => { // `related` is registry data the panel is the only consumer of; unrendered it // is dead weight the integrity spec alone keeps honest. panel.open(); - fixture.detectChanges(); + detectChanges(fixture); component.toggleEntry('task.registerVacation'); - fixture.detectChanges(); + detectChanges(fixture); const links = Array.from( fixture.nativeElement.querySelectorAll('.tp-help-entry__related-link'), @@ -306,13 +307,13 @@ describe('HelpPanelComponent', () => { it('moves the panel to a related control when its link is used', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); component.toggleEntry('task.registerVacation'); - fixture.detectChanges(); + detectChanges(fixture); const first = component.related('task.registerVacation')[0]; (fixture.nativeElement.querySelector('.tp-help-entry__related-link') as HTMLElement).click(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.expanded).toBe(first); expect(component.targetId).toBe(first); @@ -327,16 +328,16 @@ describe('HelpPanelComponent', () => { component.replayTourRequested.subscribe(tour => replays.push(tour)); // A deep link already expands its target, so there is nothing to toggle. panel.open('task.registerVacation', 'dialog'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.expanded).toBe('task.registerVacation'); (fixture.nativeElement.querySelector('.tp-help-entry__related-link') as HTMLElement).click(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.surface).toBe('dialog'); clickReplay(); - fixture.detectChanges(); + detectChanges(fixture); expect(replays).toEqual(['dialog']); }); @@ -354,7 +355,7 @@ describe('HelpPanelComponent', () => { it('names the query and offers the tasks when nothing matches', () => { panel.open(); component.onQueryChange('zzzqqq'); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isSearching).toBe(true); expect(component.isFallback).toBe(true); @@ -370,7 +371,7 @@ describe('HelpPanelComponent', () => { component.isAdmin = true; panel.open(); component.onQueryChange('payroll'); - fixture.detectChanges(); + detectChanges(fixture); const labels = Array.from(fixture.nativeElement.querySelectorAll('.tp-help-entry__kind')) .map(el => (el as HTMLElement).getAttribute('aria-label')); @@ -382,15 +383,15 @@ describe('HelpPanelComponent', () => { it('closes on Escape, and only while open', () => { document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isOpen).toBe(false); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(panelEl()).not.toBeNull(); document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isOpen).toBe(false); expect(panelEl()).toBeNull(); @@ -404,14 +405,14 @@ describe('HelpPanelComponent', () => { // key press reaches the page (from the focused element, bubbling through body) // and assert the body listener never sees it. panel.open(); - fixture.detectChanges(); + detectChanges(fixture); - const dispatcherStandIn = jest.fn(); + const dispatcherStandIn = vi.fn(); document.body.addEventListener('keydown', dispatcherStandIn); const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true }); document.body.dispatchEvent(event); document.body.removeEventListener('keydown', dispatcherStandIn); - fixture.detectChanges(); + detectChanges(fixture); expect(dispatcherStandIn).not.toHaveBeenCalled(); expect(event.defaultPrevented).toBe(true); @@ -422,7 +423,7 @@ describe('HelpPanelComponent', () => { it('lets Escape through to the page once the panel has closed', () => { // The shield must not outlive the panel, or Escape would stop closing the // day-cell dialog at all. - const dispatcherStandIn = jest.fn(); + const dispatcherStandIn = vi.fn(); document.body.addEventListener('keydown', dispatcherStandIn); document.body.dispatchEvent( new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true })); @@ -433,14 +434,14 @@ describe('HelpPanelComponent', () => { it('leaves other keys alone while open', () => { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); - const dispatcherStandIn = jest.fn(); + const dispatcherStandIn = vi.fn(); document.body.addEventListener('keydown', dispatcherStandIn); document.body.dispatchEvent( new KeyboardEvent('keydown', { key: 'a', bubbles: true, cancelable: true })); document.body.removeEventListener('keydown', dispatcherStandIn); - fixture.detectChanges(); + detectChanges(fixture); expect(dispatcherStandIn).toHaveBeenCalledTimes(1); expect(component.isOpen).toBe(true); @@ -449,14 +450,14 @@ describe('HelpPanelComponent', () => { it('rebuilds what isAdmin filters when it arrives after opening', () => { panel.open(); component.onQueryChange('payroll'); - fixture.detectChanges(); + detectChanges(fixture); expect(browsedIds()).not.toContain('toolbar.payrollExport'); expect(resultIds()).not.toContain('toolbar.payrollExport'); component.isAdmin = true; component.ngOnChanges({ isAdmin: new SimpleChange(false, true, false) }); - fixture.detectChanges(); + detectChanges(fixture); expect(browsedIds()).toContain('toolbar.payrollExport'); expect(resultIds()).toContain('toolbar.payrollExport'); @@ -466,7 +467,7 @@ describe('HelpPanelComponent', () => { // Both guards, on their false side. ngOnChanges fires once at creation with // firstChange true, before the panel has ever opened; rebuilding then would // hand *ngFor a fresh array on every open and drop focus and scroll position. - const rebuild = jest.spyOn(component as any, 'buildSections'); + const rebuild = vi.spyOn(component as any, 'buildSections'); component.isAdmin = true; component.ngOnChanges({ isAdmin: new SimpleChange(undefined, true, true) }); @@ -479,7 +480,7 @@ describe('HelpPanelComponent', () => { // A change that is neither of those does rebuild, so the test above is not // passing because buildSections is unreachable. panel.open(); - fixture.detectChanges(); + detectChanges(fixture); rebuild.mockClear(); component.ngOnChanges({ isAdmin: new SimpleChange(false, true, false) }); expect(rebuild).toHaveBeenCalled(); @@ -503,27 +504,27 @@ describe('HelpPanelComponent', () => { helpVisibility.isVisible = false; panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isOpen).toBe(false); expect(panelEl()).toBeNull(); panel.open('flex.sumFlex' as HelpEntryId); - fixture.detectChanges(); + detectChanges(fixture); expect(panelEl()).toBeNull(); // And the markup is gated too, so isOpen being set by any other route than // the service — a refactor, a test, a subclass — still renders nothing. component.isOpen = true; - fixture.detectChanges(); + detectChanges(fixture); expect(panelEl()).toBeNull(); component.isOpen = false; // An admin opens it exactly as before. helpVisibility.isVisible = true; panel.close(); - fixture.detectChanges(); + detectChanges(fixture); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(component.isOpen).toBe(true); expect(panelEl()).not.toBeNull(); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.ts index f5068bf89..d80442094 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-panel/help-panel.component.ts @@ -10,6 +10,10 @@ import { import { HelpPanelService } from '../../services/help-panel.service'; import { HelpSearchResult, HelpSearchService } from '../../services/help-search.service'; import { HelpChromeBase } from '../help-chrome.base'; +import {NgIf, NgFor, NgTemplateOutlet} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; export interface PanelSection { section: HelpSection; @@ -21,9 +25,9 @@ const SECTION_ORDER: HelpSection[] = ['task', 'toolbar', 'grid', 'dayCell', 'fle @Component({ selector: 'tp-help-panel', + imports: [NgIf, NgFor, NgTemplateOutlet, FormsModule, MatIcon, MatIconButton], templateUrl: './help-panel.component.html', styleUrls: ['./help-panel.component.scss'], - standalone: false, }) export class HelpPanelComponent extends HelpChromeBase implements OnInit, OnChanges, AfterViewChecked, OnDestroy { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.spec.ts index 34c26bcc2..5912147f3 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach} from 'vitest'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ElementRef } from '@angular/core'; import { OverlayModule } from '@angular/cdk/overlay'; @@ -18,8 +19,12 @@ const provideHelpVisibility = { provide: HelpVisibilityService, useValue: helpVi import { HelpTourService } from '../../services/help-tour.service'; import { HelpTourName } from '../../help.model'; import { enUS, enUSUi } from '../../i18n/enUS'; +import { detectChanges } from '../../../testing/detect-changes'; +import { useWorkingMutationObserver } from '../../../testing/mutation-observer'; describe('HelpTourComponent', () => { + useWorkingMutationObserver(); + const anchor = (id: string): HTMLElement => { const element = document.createElement('div'); element.setAttribute('data-tp-help', id); @@ -30,7 +35,7 @@ describe('HelpTourComponent', () => { const mount = (tour: HelpTourName = 'page'): ComponentFixture => { const fixture = TestBed.createComponent(HelpTourComponent); fixture.componentInstance.tour = tour; - fixture.detectChanges(); + detectChanges(fixture); return fixture; }; @@ -43,8 +48,7 @@ describe('HelpTourComponent', () => { document.body.querySelectorAll('[data-tp-help]').forEach(element => element.remove()); localStorage.clear(); TestBed.configureTestingModule({ - declarations: [HelpTourComponent], - imports: [OverlayModule], + imports: [HelpTourComponent, OverlayModule], providers: [ { provide: TranslateService, useValue: { currentLang: 'en-US' } }, provideHelpVisibility, @@ -78,8 +82,10 @@ describe('HelpTourComponent', () => { const dialogTour = mount('dialog'); TestBed.inject(HelpTourService).start('dialog', { isAdmin: false }); - pageTour.detectChanges(); - dialogTour.detectChanges(); + // Dialog first: a zoneless check verifies every attached view, so checking the + // page first would trip over the dialog's not-yet-refreshed step. + detectChanges(dialogTour); + detectChanges(pageTour); expect(dialogTour.componentInstance.state?.entry.id).toBe('dayCell.plannedTimes'); expect(pageTour.componentInstance.state).toBeNull(); @@ -144,7 +150,7 @@ describe('HelpTourComponent', () => { .toBe('1 / 2'); fixture.componentInstance.next(); - fixture.detectChanges(); + detectChanges(fixture); expect(card()!.querySelector('.tp-help-tour__step')!.textContent!.replace(/\s+/g, ' ').trim()) .toBe('2 / 2'); @@ -166,7 +172,7 @@ describe('HelpTourComponent', () => { const fixture = mount(); (card()!.querySelector('.tp-help-tour__next') as HTMLButtonElement).click(); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state?.entry.id).toBe('grid.openDay'); expect(fixture.componentInstance.origin?.nativeElement).toBe(second); @@ -178,7 +184,7 @@ describe('HelpTourComponent', () => { const fixture = mount(); (card()!.querySelector('.tp-help-tour__skip') as HTMLButtonElement).click(); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state).toBeNull(); expect(card()).toBeNull(); @@ -197,11 +203,11 @@ describe('HelpTourComponent', () => { document.body.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true })); press('a'); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state).not.toBeNull(); press('Escape'); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state).toBeNull(); expect(card()).toBeNull(); @@ -220,7 +226,7 @@ describe('HelpTourComponent', () => { expect(card()).not.toBeNull(); element.remove(); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state).toBeNull(); expect(fixture.componentInstance.origin).toBeNull(); @@ -239,7 +245,7 @@ describe('HelpTourComponent', () => { expect(card()).not.toBeNull(); element.remove(); - fixture.detectChanges(); + detectChanges(fixture); expect(tour.isRunning).toBe(false); expect(tour.hasSeen('dialog')).toBe(false); @@ -247,12 +253,12 @@ describe('HelpTourComponent', () => { // Still offerable: the same tour runs again and renders. element = anchor('dayCell.plannedTimes'); tour.start('dialog', { isAdmin: false }); - fixture.detectChanges(); + detectChanges(fixture); expect(card()).not.toBeNull(); // The other direction: the user ending it does count. (card()!.querySelector('.tp-help-tour__skip') as HTMLButtonElement).click(); - fixture.detectChanges(); + detectChanges(fixture); expect(card()).toBeNull(); expect(tour.hasSeen('dialog')).toBe(true); @@ -265,7 +271,7 @@ describe('HelpTourComponent', () => { element.remove(); const replacement = anchor('toolbar.dateRange'); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.origin?.nativeElement).toBe(replacement); expect(card()).not.toBeNull(); @@ -277,7 +283,7 @@ describe('HelpTourComponent', () => { const fixture = mount(); fixture.componentInstance.next(); - fixture.detectChanges(); + detectChanges(fixture); expect(fixture.componentInstance.state).toBeNull(); expect(fixture.componentInstance.origin).toBeNull(); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.ts index 95d6964db..f2c039876 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/components/help-tour/help-tour.component.ts @@ -13,15 +13,16 @@ import { Subscription } from 'rxjs'; import { HelpProse, HelpTourName } from '../../help.model'; import { HelpTourService, HelpTourState } from '../../services/help-tour.service'; import { HelpChromeBase } from '../help-chrome.base'; +import {NgIf} from '@angular/common'; /** Distinguishes the aria-labelledby target of one mounted tour from another's. */ let nextTourCardId = 0; @Component({ selector: 'tp-help-tour', + imports: [NgIf, CdkConnectedOverlay], templateUrl: './help-tour.component.html', styleUrls: ['./help-tour.component.scss'], - standalone: false, }) export class HelpTourComponent extends HelpChromeBase implements OnInit, DoCheck, AfterViewChecked, OnDestroy { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/grid-help-anchors.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/grid-help-anchors.ts index a1215b344..2518c319b 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/grid-help-anchors.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/grid-help-anchors.ts @@ -1,4 +1,5 @@ import { isDevMode } from '@angular/core'; +import { HelpEntry } from './help.model'; import { PLANNING_HELP_ENTRIES } from './planning-help.registry'; /** @@ -10,12 +11,21 @@ import { PLANNING_HELP_ENTRIES } from './planning-help.registry'; export const GRID_NAME_HEADER_SELECTOR = 'th.mat-column-siteName'; /** Read from the registry rather than repeated, so the stamp cannot drift from it. */ -const SORT_NAME_ANCHOR = PLANNING_HELP_ENTRIES.find(entry => entry.id === 'grid.sortName')?.anchor; +const findSortNameAnchor = (entries: readonly HelpEntry[]) => + entries.find(entry => entry.id === 'grid.sortName')?.anchor; +const SORT_NAME_ANCHOR = findSortNameAnchor(PLANNING_HELP_ENTRIES); let warnedAboutMissingAnchor = false; -export function applyGridHelpAnchors(root: ParentNode | null | undefined): void { - if (!SORT_NAME_ANCHOR) { +/** + * `entries` exists for specs: the app always stamps from PLANNING_HELP_ENTRIES. + */ +export function applyGridHelpAnchors( + root: ParentNode | null | undefined, + entries: readonly HelpEntry[] = PLANNING_HELP_ENTRIES, +): void { + const sortNameAnchor = entries === PLANNING_HELP_ENTRIES ? SORT_NAME_ANCHOR : findSortNameAnchor(entries); + if (!sortNameAnchor) { // A stamp that quietly stops happening is worse than one that complains: // the registry would still advertise an anchor nothing produces. if (isDevMode() && !warnedAboutMissingAnchor) { @@ -29,6 +39,6 @@ export function applyGridHelpAnchors(root: ParentNode | null | undefined): void } const header = root?.querySelector(GRID_NAME_HEADER_SELECTOR); if (header && !header.hasAttribute('data-tp-help')) { - header.setAttribute('data-tp-help', SORT_NAME_ANCHOR); + header.setAttribute('data-tp-help', sortNameAnchor); } } diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/help-wiring.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/help-wiring.spec.ts index ad48e46c8..82dd5a2c8 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/help-wiring.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/help-wiring.spec.ts @@ -1,4 +1,6 @@ +import {describe, it, expect, beforeEach, beforeAll, afterAll, vi} from 'vitest'; import { readdirSync, readFileSync } from 'fs'; +import { createRequire } from 'module'; import { dirname, join } from 'path'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; @@ -15,8 +17,9 @@ import { HelpTourService } from './services/help-tour.service'; import { applyGridHelpAnchors } from './grid-help-anchors'; import { HelpVisibilityService } from './services/help-visibility.service'; import { enUSUi } from './i18n/enUS'; +import { CLIENT_ROOT, MODULE_ROOT } from '../testing/source-paths'; +import { detectChanges } from '../testing/detect-changes'; -const MODULE_ROOT = join(__dirname, '..'); /** Help is admin-only; the components under test need the flag, not a whole store. */ const provideHelpVisibility = { @@ -289,24 +292,20 @@ describe('help wiring', () => { }); it('warns instead of stopping silently if the registry drops the sortName anchor', () => { - jest.isolateModules(() => { - jest.doMock('./planning-help.registry', () => ({ PLANNING_HELP_ENTRIES: [] })); - const warn = jest.spyOn(console, 'warn').mockImplementation(() => undefined); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { applyGridHelpAnchors: stamp } = require('./grid-help-anchors'); - - const root = document.createElement('div'); - const header = document.createElement('th'); - header.className = 'mat-column-siteName'; - root.appendChild(header); - - stamp(root); - - expect(header.hasAttribute('data-tp-help')).toBe(false); - expect(warn).toHaveBeenCalledWith(expect.stringContaining('grid.sortName')); - warn.mockRestore(); - }); - jest.dontMock('./planning-help.registry'); + // An empty registry stands in for one that lost the sortName entry + // (module mocks of relative files don't apply under Angular's test builder). + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + + const root = document.createElement('div'); + const header = document.createElement('th'); + header.className = 'mat-column-siteName'; + root.appendChild(header); + + applyGridHelpAnchors(root, []); + + expect(header.hasAttribute('data-tp-help')).toBe(false); + expect(warn).toHaveBeenCalledWith(expect.stringContaining('grid.sortName')); + warn.mockRestore(); }); it('calls the header stamp from the table component', () => { @@ -324,7 +323,7 @@ describe('help wiring', () => { // loses hit-testing to it — a click on the panel would reach the backdrop // and close the dialog. The CDK value is read, not assumed. const cdkCss = readFileSync( - join(dirname(require.resolve('@angular/cdk/package.json')), 'overlay-prebuilt.css'), 'utf8'); + join(dirname(createRequire(join(CLIENT_ROOT, 'package.json')).resolve('@angular/cdk/package.json')), 'overlay-prebuilt.css'), 'utf8'); const cdkLayers = [...cdkCss.matchAll( /\.cdk-(?:overlay-backdrop|overlay-pane|global-overlay-wrapper)\{[^}]*?z-index:\s*(\d+)/g)] .map(match => Number(match[1])); @@ -365,8 +364,7 @@ describe('help deep link and tour scrolling', () => { const mountPanel = (): ComponentFixture => { TestBed.configureTestingModule({ - declarations: [HelpPanelComponent], - imports: [FormsModule, MatIconModule, MatButtonModule], + imports: [HelpPanelComponent, FormsModule, MatIconModule, MatButtonModule], providers: [ HelpPanelService, { provide: TranslateService, useValue: { currentLang: 'en-US' } }, @@ -374,7 +372,7 @@ describe('help deep link and tour scrolling', () => { ], }); const fixture = TestBed.createComponent(HelpPanelComponent); - fixture.detectChanges(); + detectChanges(fixture); return fixture; }; @@ -384,15 +382,15 @@ describe('help deep link and tour scrolling', () => { // Browsing the whole catalogue has nothing to scroll to. panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(scrolled).toHaveLength(0); // flex.sumFlex sits in the last section, well below the fold of a panel that // opens scrolled to the top. panel.close(); - fixture.detectChanges(); + detectChanges(fixture); panel.open('flex.sumFlex'); - fixture.detectChanges(); + detectChanges(fixture); const target = fixture.nativeElement.querySelector('.tp-help-entry--target') as HTMLElement; expect(target).not.toBeNull(); @@ -410,14 +408,14 @@ describe('help deep link and tour scrolling', () => { expect(host.parentNode).not.toBe(container); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); expect(host.parentNode).toBe(container); expect(document.activeElement) .toBe(host.querySelector('.tp-help-panel__search input')); panel.close(); - fixture.detectChanges(); + detectChanges(fixture); expect(host.parentNode).not.toBe(container); }); @@ -433,9 +431,9 @@ describe('help deep link and tour scrolling', () => { for (let cycle = 0; cycle < 2; cycle++) { panel.open(); - fixture.detectChanges(); + detectChanges(fixture); panel.close(); - fixture.detectChanges(); + detectChanges(fixture); } expect(host.nextSibling).toBe(marker); @@ -450,7 +448,7 @@ describe('help deep link and tour scrolling', () => { const panel = TestBed.inject(HelpPanelService); panel.open(); - fixture.detectChanges(); + detectChanges(fixture); const search = host.querySelector('.tp-help-panel__search input') as HTMLElement; expect(document.activeElement).toBe(search); @@ -459,12 +457,12 @@ describe('help deep link and tour scrolling', () => { const entryHead = host.querySelector('.tp-help-entry__head') as HTMLElement; entryHead.focus(); panel.open('flex.sumFlex'); - fixture.detectChanges(); + detectChanges(fixture); expect(document.activeElement).toBe(entryHead); // Closing hands focus back to whatever opened it. panel.close(); - fixture.detectChanges(); + detectChanges(fixture); expect(document.activeElement).toBe(trigger); trigger.remove(); @@ -472,8 +470,7 @@ describe('help deep link and tour scrolling', () => { it('scrolls the current tour anchor into view when a step becomes current', () => { TestBed.configureTestingModule({ - declarations: [HelpTourComponent], - imports: [OverlayModule], + imports: [HelpTourComponent, OverlayModule], providers: [ { provide: TranslateService, useValue: { currentLang: 'en-US' } }, provideHelpVisibility, @@ -489,18 +486,18 @@ describe('help deep link and tour scrolling', () => { const fixture: ComponentFixture = TestBed.createComponent(HelpTourComponent); fixture.componentInstance.tour = 'page'; - fixture.detectChanges(); + detectChanges(fixture); const tour = TestBed.inject(HelpTourService); tour.start('page', { isAdmin: true }); - fixture.detectChanges(); + detectChanges(fixture); const first = anchors.get('toolbar.dateRange') as HTMLElement; expect(scrolled).toContain(first); scrolled.length = 0; tour.next(); - fixture.detectChanges(); + detectChanges(fixture); expect(scrolled).toContain(anchors.get('toolbar.navForward') as HTMLElement); expect(scrolled).not.toContain(first); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/da.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/da.spec.ts index 5609bc7b4..4a7fc3f1c 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/da.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/da.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { da, daUi } from './da'; import { enUS, enUSUi } from './enUS'; import { HELP_IDS, HelpUiKey } from '../help.model'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/day-type-copy.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/day-type-copy.spec.ts index ce142c675..73994db13 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/day-type-copy.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/i18n/day-type-copy.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { HELP_LOCALES } from './index'; import { HelpProse } from '../help.model'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/planning-help.registry.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/planning-help.registry.spec.ts index 26723ab7f..ab5a765d6 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/planning-help.registry.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/planning-help.registry.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { PLANNING_HELP_ENTRIES } from './planning-help.registry'; import { enUS } from './i18n/enUS'; import { HelpEntry, HelpEntryId } from './help.model'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/playwright-tour-seed.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/playwright-tour-seed.spec.ts index 91cb0795a..e4381e22e 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/playwright-tour-seed.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/playwright-tour-seed.spec.ts @@ -1,6 +1,8 @@ +import {describe, it, expect} from 'vitest'; import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; import { TOUR_STORAGE_KEY } from './services/help-tour.service'; +import { CLIENT_ROOT } from '../testing/source-paths'; /** * The planning page starts its onboarding tours automatically the first time a @@ -15,7 +17,6 @@ import { TOUR_STORAGE_KEY } from './services/help-tour.service'; * exists and names the key HelpTourService actually reads, and the config points * at the file. */ -const CLIENT_ROOT = join(__dirname, '..', '..', '..', '..', '..', '..'); const SEED_RELATIVE = 'playwright/helpers/tour-seen.storage.json'; const SEED_PATH = join(CLIENT_ROOT, SEED_RELATIVE); const CONFIG_PATH = join(CLIENT_ROOT, 'playwright.config.ts'); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-content.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-content.service.spec.ts index 665ac8c33..83b27f6ad 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-content.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-content.service.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; import { HelpContentService } from './help-content.service'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-panel.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-panel.service.spec.ts index 9fc4697e7..456ef0dcc 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-panel.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-panel.service.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach} from 'vitest'; import { firstValueFrom } from 'rxjs'; import { HelpEntryId } from '../help.model'; import { HelpPanelService } from './help-panel.service'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-search.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-search.service.spec.ts index f93ac26e8..e499349d6 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-search.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-search.service.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; import { HelpSearchService } from './help-search.service'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-tour.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-tour.service.spec.ts index 300f0fcc7..12fcae5ab 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-tour.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-tour.service.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; import { firstValueFrom, of } from 'rxjs'; @@ -247,7 +248,7 @@ describe('HelpTourService', () => { }); it('survives localStorage being unavailable', () => { - const getItem = jest.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { + const getItem = vi.spyOn(Storage.prototype, 'getItem').mockImplementation(() => { throw new Error('blocked'); }); expect(service.hasSeen('page')).toBe(false); @@ -255,7 +256,7 @@ describe('HelpTourService', () => { }); it('survives localStorage rejecting a write', () => { - const setItem = jest.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { + const setItem = vi.spyOn(Storage.prototype, 'setItem').mockImplementation(() => { throw new Error('quota'); }); expect(() => service.markSeen('page')).not.toThrow(); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-visibility.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-visibility.service.spec.ts index ad21eb068..dfa862452 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-visibility.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/help/services/help-visibility.service.spec.ts @@ -1,3 +1,4 @@ +import {describe, it, expect, beforeEach} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { Store } from '@ngrx/store'; import { BehaviorSubject, firstValueFrom } from 'rxjs'; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/layouts/time-planning-pn-layout.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/layouts/time-planning-pn-layout.component.ts index 45492509a..3f656762e 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/layouts/time-planning-pn-layout.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/layouts/time-planning-pn-layout.component.ts @@ -6,12 +6,13 @@ import {translates} from './../i18n/translates'; import {Store} from '@ngrx/store'; import {addPluginToVisited, selectPluginsVisitedPlugins} from 'src/app/state'; import {take} from 'rxjs'; +import {RouterOutlet} from '@angular/router'; @Component({ selector: 'app-time-planning-pn-layout', + imports: [RouterOutlet], template: ` `, - standalone: false }) export class TimePlanningPnLayoutComponent implements AfterContentInit, OnInit { private translateService = inject(TranslateService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/absence-requests.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/absence-requests.module.ts index a43436a3e..b26f49bac 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/absence-requests.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/absence-requests.module.ts @@ -1,16 +1,6 @@ import { NgModule } from '@angular/core'; -import { CommonModule, NgIf, AsyncPipe, DatePipe } from '@angular/common'; -import { EformSharedModule } from 'src/app/common/modules/eform-shared/eform-shared.module'; -import { MatButton, MatIconButton } from '@angular/material/button'; -import { TranslateModule } from '@ngx-translate/core'; +import {MatDialogModule} from '@angular/material/dialog'; import { AbsenceRequestsRouting } from './absence-requests.routing'; -import { MtxGrid } from '@ng-matero/extensions/grid'; -import { MatDialogActions, MatDialogContent, MatDialogTitle } from '@angular/material/dialog'; -import { MatIcon } from '@angular/material/icon'; -import { MatTooltip } from '@angular/material/tooltip'; -import { FormsModule } from '@angular/forms'; -import { MatFormField, MatLabel } from '@angular/material/form-field'; -import { MatInput } from '@angular/material/input'; import { AbsenceRequestsContainerComponent, AbsenceRequestsTableComponent, @@ -20,27 +10,8 @@ import { @NgModule({ imports: [ - CommonModule, - AsyncPipe, - DatePipe, - NgIf, - EformSharedModule, - MatButton, - TranslateModule, + MatDialogModule, AbsenceRequestsRouting, - MtxGrid, - MatDialogTitle, - MatDialogContent, - MatDialogActions, - MatIcon, - MatIconButton, - MatTooltip, - FormsModule, - MatFormField, - MatInput, - MatLabel - ], - declarations: [ AbsenceRequestsContainerComponent, AbsenceRequestsTableComponent, AbsenceRequestsApproveModalComponent, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-approve-modal/absence-requests-approve-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-approve-modal/absence-requests-approve-modal.component.ts index 7cf97a3cd..a171149cc 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-approve-modal/absence-requests-approve-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-approve-modal/absence-requests-approve-modal.component.ts @@ -1,17 +1,21 @@ import { Component, OnInit, inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { TimePlanningPnAbsenceRequestsService } from 'src/app/plugins/modules/time-planning-pn/services'; import { AbsenceRequestModel, AbsenceRequestDecisionModel } from 'src/app/plugins/modules/time-planning-pn/models'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { selectAuthUser } from 'src/app/state'; import { take } from 'rxjs'; +import {NgIf, DatePipe} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-absence-requests-approve-modal', + imports: [NgIf, DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './absence-requests-approve-modal.component.html', - standalone: false }) export class AbsenceRequestsApproveModalComponent implements OnInit { private absenceRequestsService = inject(TimePlanningPnAbsenceRequestsService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-reject-modal/absence-requests-reject-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-reject-modal/absence-requests-reject-modal.component.ts index 36fbb4d6c..7f32bf862 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-reject-modal/absence-requests-reject-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-actions/absence-requests-reject-modal/absence-requests-reject-modal.component.ts @@ -1,17 +1,21 @@ import { Component, OnInit, inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { TimePlanningPnAbsenceRequestsService } from 'src/app/plugins/modules/time-planning-pn/services'; import { AbsenceRequestModel, AbsenceRequestDecisionModel } from 'src/app/plugins/modules/time-planning-pn/models'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { selectAuthUser } from 'src/app/state'; import { take } from 'rxjs'; +import {NgIf, DatePipe} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-absence-requests-reject-modal', + imports: [NgIf, DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './absence-requests-reject-modal.component.html', - standalone: false }) export class AbsenceRequestsRejectModalComponent implements OnInit { private absenceRequestsService = inject(TimePlanningPnAbsenceRequestsService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-container/absence-requests-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-container/absence-requests-container.component.ts index 6246b51f2..b20c725c9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-container/absence-requests-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-container/absence-requests-container.component.ts @@ -6,16 +6,18 @@ import { MatDialog } from '@angular/material/dialog'; import { Overlay } from '@angular/cdk/overlay'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { selectAuthUser } from 'src/app/state'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; +import {AbsenceRequestsTableComponent} from '../absence-requests-table/absence-requests-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-absence-requests-container', + imports: [TranslatePipe, EformNewSubheaderComponent, AbsenceRequestsTableComponent], templateUrl: './absence-requests-container.component.html', styleUrls: ['./absence-requests-container.component.scss'], - standalone: false }) export class AbsenceRequestsContainerComponent implements OnInit, OnDestroy { public dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-table/absence-requests-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-table/absence-requests-table.component.ts index cb4e931b3..c87317571 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-table/absence-requests-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/absence-requests/components/absence-requests-table/absence-requests-table.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnInit, OnDestroy, Output, inject } from '@angular/core'; -import { MtxGridColumn } from '@ng-matero/extensions/grid'; -import { TranslateService } from '@ngx-translate/core'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { AbsenceRequestModel } from '../../../../models'; import { Subscription } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; @@ -11,12 +11,16 @@ import { AbsenceRequestsApproveModalComponent, AbsenceRequestsRejectModalComponent } from '../absence-requests-actions'; +import {NgIf, NgClass} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; @AutoUnsubscribe() @Component({ selector: 'app-absence-requests-table', + imports: [NgIf, NgClass, TranslatePipe, MatIcon, MatIconButton, MtxGrid, MatTooltip], templateUrl: './absence-requests-table.component.html', - standalone: false }) export class AbsenceRequestsTableComponent implements OnInit, OnDestroy { private dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/break-policies.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/break-policies.module.ts index a72ee710e..b298b54e9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/break-policies.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/break-policies.module.ts @@ -1,9 +1,4 @@ -import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {RouterModule} from '@angular/router'; -import {TranslateModule} from '@ngx-translate/core'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; import {BreakPoliciesRouting} from './break-policies.routing'; import { BreakPoliciesContainerComponent, @@ -16,38 +11,12 @@ import { BreakPolicyRuleDialogComponent, } from './components'; import {TimePlanningPnBreakPoliciesService} from '../../services'; -import {MtxGridModule} from '@ng-matero/extensions/grid'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import {MatButtonModule} from '@angular/material/button'; -import {MatIconModule} from '@angular/material/icon'; import {MatDialogModule} from '@angular/material/dialog'; -import {MatTooltipModule} from '@angular/material/tooltip'; -import {MatSelectModule} from '@angular/material/select'; -import {MatMenuModule} from '@angular/material/menu'; -import {MatTableModule} from '@angular/material/table'; @NgModule({ imports: [ - CommonModule, - TranslateModule, - FormsModule, - EformSharedModule, - RouterModule, - ReactiveFormsModule, BreakPoliciesRouting, - MtxGridModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, MatDialogModule, - MatTooltipModule, - MatSelectModule, - MatMenuModule, - MatTableModule, - ], - declarations: [ BreakPoliciesContainerComponent, BreakPoliciesTableComponent, BreakPoliciesCreateModalComponent, @@ -62,5 +31,3 @@ import {MatTableModule} from '@angular/material/table'; ], }) export class BreakPoliciesModule {} - - diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-container/break-policies-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-container/break-policies-container.component.ts index c8f798db5..f888170cd 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-container/break-policies-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-container/break-policies-container.component.ts @@ -10,13 +10,15 @@ import { TimePlanningPnBreakPoliciesService } from '../../../../services'; import { BreakPoliciesCreateModalComponent } from '../break-policies-create-modal/break-policies-create-modal.component'; import { BreakPoliciesEditModalComponent } from '../break-policies-edit-modal/break-policies-edit-modal.component'; import { BreakPoliciesDeleteModalComponent } from '../break-policies-delete-modal/break-policies-delete-modal.component'; +import {TranslatePipe} from '@ngx-translate/core'; +import {BreakPoliciesTableComponent} from '../break-policies-table/break-policies-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-break-policies-container', + imports: [TranslatePipe, BreakPoliciesTableComponent], templateUrl: './break-policies-container.component.html', styleUrls: ['./break-policies-container.component.scss'], - standalone: false }) export class BreakPoliciesContainerComponent implements OnInit, OnDestroy { private breakPoliciesService = inject(TimePlanningPnBreakPoliciesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-create-modal/break-policies-create-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-create-modal/break-policies-create-modal.component.ts index c88c14edc..289ecdc86 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-create-modal/break-policies-create-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-create-modal/break-policies-create-modal.component.ts @@ -1,17 +1,21 @@ import { Component, OnInit, inject } from '@angular/core'; -import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms'; -import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import {FormBuilder, FormGroup, FormArray, Validators, ReactiveFormsModule} from '@angular/forms'; +import {MatDialog, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { BreakPolicyCreateModel } from '../../../../models'; import { TimePlanningPnBreakPoliciesService } from '../../../../services'; import { BreakPolicyRuleDialogComponent, BreakPolicyRuleDialogData } from '../break-policy-rule-dialog/break-policy-rule-dialog.component'; -import { BreakPolicyRuleFormValue } from '../break-policy-rules-list/break-policy-rules-list.component'; +import {BreakPolicyRuleFormValue, BreakPolicyRulesListComponent} from '../break-policy-rules-list/break-policy-rules-list.component'; +import {NgIf} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-break-policies-create-modal', + imports: [NgIf, ReactiveFormsModule, TranslatePipe, MatFormField, MatLabel, MatError, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions, BreakPolicyRulesListComponent], templateUrl: './break-policies-create-modal.component.html', styleUrls: ['./break-policies-create-modal.component.scss'], - standalone: false }) export class BreakPoliciesCreateModalComponent implements OnInit { private breakPoliciesService = inject(TimePlanningPnBreakPoliciesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-delete-modal/break-policies-delete-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-delete-modal/break-policies-delete-modal.component.ts index 67f447675..140ab1d8b 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-delete-modal/break-policies-delete-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-delete-modal/break-policies-delete-modal.component.ts @@ -1,14 +1,15 @@ import { Component, OnInit, inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { BreakPolicyModel } from '../../../../models'; import { TimePlanningPnBreakPoliciesService } from '../../../../services'; +import {TranslatePipe} from '@ngx-translate/core'; @Component({ selector: 'app-break-policies-delete-modal', + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './break-policies-delete-modal.component.html', styleUrls: ['./break-policies-delete-modal.component.scss'], - standalone: false }) export class BreakPoliciesDeleteModalComponent implements OnInit { private breakPoliciesService = inject(TimePlanningPnBreakPoliciesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-edit-modal/break-policies-edit-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-edit-modal/break-policies-edit-modal.component.ts index 325cedec4..4113fa2bb 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-edit-modal/break-policies-edit-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-edit-modal/break-policies-edit-modal.component.ts @@ -1,17 +1,21 @@ import { Component, OnInit, inject } from '@angular/core'; -import { FormBuilder, FormGroup, FormArray, Validators } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import {FormBuilder, FormGroup, FormArray, Validators, ReactiveFormsModule} from '@angular/forms'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; import { BreakPolicyModel, BreakPolicyUpdateModel } from '../../../../models'; import { TimePlanningPnBreakPoliciesService } from '../../../../services'; import { BreakPolicyRuleDialogComponent, BreakPolicyRuleDialogData } from '../break-policy-rule-dialog/break-policy-rule-dialog.component'; -import { BreakPolicyRuleFormValue } from '../break-policy-rules-list/break-policy-rules-list.component'; +import {BreakPolicyRuleFormValue, BreakPolicyRulesListComponent} from '../break-policy-rules-list/break-policy-rules-list.component'; +import {NgIf} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-break-policies-edit-modal', + imports: [NgIf, ReactiveFormsModule, TranslatePipe, MatFormField, MatLabel, MatError, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions, BreakPolicyRulesListComponent], templateUrl: './break-policies-edit-modal.component.html', styleUrls: ['./break-policies-edit-modal.component.scss'], - standalone: false }) export class BreakPoliciesEditModalComponent implements OnInit { private breakPoliciesService = inject(TimePlanningPnBreakPoliciesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.html b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.html index 799ac1b3b..95b8f726b 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.html +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.html @@ -20,7 +20,7 @@
- diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.ts index 0bcb82946..6355880b1 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policies-table/break-policies-table.component.ts @@ -1,13 +1,18 @@ import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { MtxGridColumn } from '@ng-matero/extensions/grid'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; import { BreakPolicySimpleModel } from '../../../../models'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton, MatButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; @Component({ selector: 'app-break-policies-table', + imports: [TranslatePipe, MatIcon, MatIconButton, MatButton, MtxGrid, MatTooltip, MatMenu, MatMenuItem, MatMenuTrigger], templateUrl: './break-policies-table.component.html', styleUrls: ['./break-policies-table.component.scss'], - standalone: false }) export class BreakPoliciesTableComponent { private dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-dialog/break-policy-rule-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-dialog/break-policy-rule-dialog.component.ts index 2d1371fda..43a8b3817 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-dialog/break-policy-rule-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-dialog/break-policy-rule-dialog.component.ts @@ -1,7 +1,9 @@ import {Component, Inject, OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {FormGroup, FormControl, Validators} from '@angular/forms'; import {BreakPolicyRuleFormValue} from '../break-policy-rules-list/break-policy-rules-list.component'; +import {TranslatePipe} from '@ngx-translate/core'; +import {BreakPolicyRuleFormComponent} from '../break-policy-rule-form/break-policy-rule-form.component'; export interface BreakPolicyRuleDialogData { mode: 'create' | 'edit'; @@ -10,7 +12,7 @@ export interface BreakPolicyRuleDialogData { @Component({ selector: 'app-break-policy-rule-dialog', - standalone: false, + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions, BreakPolicyRuleFormComponent], templateUrl: './break-policy-rule-dialog.component.html', styleUrls: ['./break-policy-rule-dialog.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-form/break-policy-rule-form.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-form/break-policy-rule-form.component.ts index 0af47b333..6165d3535 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-form/break-policy-rule-form.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rule-form/break-policy-rule-form.component.ts @@ -1,9 +1,15 @@ import {Component, Input, OnInit} from '@angular/core'; -import {FormGroup, FormControl, Validators} from '@angular/forms'; +import {FormGroup, FormControl, Validators, ReactiveFormsModule} from '@angular/forms'; +import {NgIf, NgFor} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel, MatError, MatHint} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MatSelect} from '@angular/material/select'; +import {MatOption} from '@angular/material/core'; @Component({ selector: 'app-break-policy-rule-form', - standalone: false, + imports: [NgIf, NgFor, ReactiveFormsModule, TranslatePipe, MatFormField, MatLabel, MatError, MatHint, MatInput, MatSelect, MatOption], templateUrl: './break-policy-rule-form.component.html', styleUrls: ['./break-policy-rule-form.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rules-list/break-policy-rules-list.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rules-list/break-policy-rules-list.component.ts index aa3de9002..452be4eb6 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rules-list/break-policy-rules-list.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/break-policies/components/break-policy-rules-list/break-policy-rules-list.component.ts @@ -1,5 +1,11 @@ import {Component, EventEmitter, Input, Output} from '@angular/core'; import {FormArray, FormGroup} from '@angular/forms'; +import {NgIf} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton, MatButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatTableModule} from '@angular/material/table'; export interface BreakPolicyRuleFormValue { id?: number | null; @@ -10,7 +16,7 @@ export interface BreakPolicyRuleFormValue { @Component({ selector: 'app-break-policy-rules-list', - standalone: false, + imports: [NgIf, TranslatePipe, MatIcon, MatIconButton, MatButton, MatTooltip, MatTableModule], templateUrl: './break-policy-rules-list.component.html', styleUrls: ['./break-policy-rules-list.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-all-update-modal/time-flexes-comment-office-all-update-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-all-update-modal/time-flexes-comment-office-all-update-modal.component.ts index 118b63d68..1462429ff 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-all-update-modal/time-flexes-comment-office-all-update-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-all-update-modal/time-flexes-comment-office-all-update-modal.component.ts @@ -6,15 +6,20 @@ import { } from '@angular/core'; import {AutoUnsubscribe} from 'ngx-auto-unsubscribe'; import {TimeFlexesModel} from '../../../../../models'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; +import {DatePipe} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @AutoUnsubscribe() @Component({ selector: 'app-time-flexes-comment-office-all-update-modal', + imports: [DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './time-flexes-comment-office-all-update-modal.component.html', styleUrls: ['./time-flexes-comment-office-all-update-modal.component.scss', ], - standalone: false }) export class TimeFlexesCommentOfficeAllUpdateModalComponent implements OnInit, OnDestroy { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-update-modal/time-flexes-comment-office-update-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-update-modal/time-flexes-comment-office-update-modal.component.ts index e13cb77fa..5b400040a 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-update-modal/time-flexes-comment-office-update-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-actions/comment-office-update-modal/time-flexes-comment-office-update-modal.component.ts @@ -6,14 +6,19 @@ import { } from '@angular/core'; import {AutoUnsubscribe} from 'ngx-auto-unsubscribe'; import {TimeFlexesModel} from '../../../../../models'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; +import {DatePipe} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @AutoUnsubscribe() @Component({ selector: 'app-time-flexes-comment-office-update-modal', + imports: [DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './time-flexes-comment-office-update-modal.component.html', styleUrls: ['./time-flexes-comment-office-update-modal.component.scss'], - standalone: false }) export class TimeFlexesCommentOfficeUpdateModalComponent implements OnInit, OnDestroy { public dialogRef = inject(MatDialogRef); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-container/time-flexes-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-container/time-flexes-container.component.ts index 9c3724317..2298690a9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-container/time-flexes-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-container/time-flexes-container.component.ts @@ -10,13 +10,18 @@ import { TimeFlexesUpdateModel, } from '../../../../models'; import { TimePlanningPnFlexesService } from '../../../../services'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatTooltip} from '@angular/material/tooltip'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; +import {TimeFlexesTableComponent} from '../time-flexes-table/time-flexes-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-time-flexes-container', + imports: [TranslatePipe, MatIcon, MatTooltip, EformNewSubheaderComponent, TimeFlexesTableComponent], templateUrl: './time-flexes-container.component.html', styleUrls: ['./time-flexes-container.component.scss'], - standalone: false }) export class TimeFlexesContainerComponent implements OnInit, OnDestroy { private planningsService = inject(TimePlanningPnFlexesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.html b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.html index 9df9510c4..d2c7bea62 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.html +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.html @@ -9,24 +9,24 @@ [showColumnMenuButton]="false"> - - + + - - + + diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.ts index 16dc198ec..7f097a633 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/components/time-flexes-table/time-flexes-table.component.ts @@ -13,19 +13,22 @@ import { TimeFlexesCommentOfficeUpdateModalComponent, } from '../'; import {TranslateService} from '@ngx-translate/core'; -import {MtxGridColumn} from '@ng-matero/extensions/grid'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; import {MatDialog} from '@angular/material/dialog'; import {Overlay} from '@angular/cdk/overlay'; import {dialogConfigHelper} from 'src/app/common/helpers'; import {Subscription} from 'rxjs'; import {AutoUnsubscribe} from 'ngx-auto-unsubscribe'; +import {FormsModule} from '@angular/forms'; +import {MatFormField} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @AutoUnsubscribe() @Component({ selector: 'app-time-flexes-table', + imports: [FormsModule, MatFormField, MatInput, MtxGrid], templateUrl: './time-flexes-table.component.html', styleUrls: ['./time-flexes-table.component.scss'], - standalone: false }) export class TimeFlexesTableComponent implements OnInit, OnDestroy { private translateService = inject(TranslateService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/flex.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/flex.module.ts index 4ae2f8783..3606e96f9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/flex.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/flexes/flex.module.ts @@ -1,9 +1,4 @@ -import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {RouterModule} from '@angular/router'; -import {TranslateModule} from '@ngx-translate/core'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; import {FlexRouting} from './flex.routing'; import { TimeFlexesCommentOfficeAllUpdateModalComponent, @@ -11,32 +6,12 @@ import { TimeFlexesContainerComponent, TimeFlexesTableComponent, } from './components'; -import {MtxGridModule} from '@ng-matero/extensions/grid'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import {MatButtonModule} from '@angular/material/button'; -import {MatIconModule} from '@angular/material/icon'; import {MatDialogModule} from '@angular/material/dialog'; -import {MatTooltipModule} from '@angular/material/tooltip'; @NgModule({ imports: [ - CommonModule, - TranslateModule, - FormsModule, - EformSharedModule, - RouterModule, - ReactiveFormsModule, FlexRouting, - MtxGridModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, MatDialogModule, - MatTooltipModule, - ], - declarations: [ TimeFlexesCommentOfficeAllUpdateModalComponent, TimeFlexesCommentOfficeUpdateModalComponent, TimeFlexesContainerComponent, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-dialog/day-type-rule-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-dialog/day-type-rule-dialog.component.ts index 904b1e85a..6d89142fc 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-dialog/day-type-rule-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-dialog/day-type-rule-dialog.component.ts @@ -1,7 +1,17 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { MatTableDataSource } from '@angular/material/table'; +import {AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, Validators, ReactiveFormsModule} from '@angular/forms'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; +import {MatTableDataSource, MatTableModule} from '@angular/material/table'; +import {NgIf, NgFor} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatSelect} from '@angular/material/select'; +import {MatOption} from '@angular/material/core'; +import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; export interface DayTypeRuleDialogData { mode: 'create' | 'edit'; @@ -10,7 +20,7 @@ export interface DayTypeRuleDialogData { @Component({ selector: 'app-day-type-rule-dialog', - standalone: false, + imports: [NgIf, NgFor, ReactiveFormsModule, TranslatePipe, MatIcon, MatIconButton, MatFormField, MatLabel, MatError, MatInput, MatTooltip, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatSelect, MatOption, MatTableModule, NgxMaterialTimepickerModule], templateUrl: './day-type-rule-dialog.component.html', styleUrls: ['./day-type-rule-dialog.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-list/day-type-rule-list.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-list/day-type-rule-list.component.ts index b91ae9ff1..5b1740afb 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-list/day-type-rule-list.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/day-type-rule-list/day-type-rule-list.component.ts @@ -1,10 +1,15 @@ import { Component, DoCheck, EventEmitter, Input, Output } from '@angular/core'; import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; -import { MatTableDataSource } from '@angular/material/table'; +import {MatTableDataSource, MatTableModule} from '@angular/material/table'; +import {NgIf} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; @Component({ selector: 'app-day-type-rule-list', - standalone: false, + imports: [NgIf, TranslatePipe, MatIcon, MatIconButton, MatTooltip, MatTableModule], templateUrl: './day-type-rule-list.component.html', styleUrls: ['./day-type-rule-list.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-dialog/pay-day-rule-dialog.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-dialog/pay-day-rule-dialog.component.ts index 513e218a2..2f7ac734c 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-dialog/pay-day-rule-dialog.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-dialog/pay-day-rule-dialog.component.ts @@ -1,6 +1,8 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; +import {TranslatePipe} from '@ngx-translate/core'; +import {PayDayRuleFormComponent} from '../pay-day-rule-form/pay-day-rule-form.component'; export interface PayDayRuleDialogData { mode: 'create' | 'edit'; @@ -9,7 +11,7 @@ export interface PayDayRuleDialogData { @Component({ selector: 'app-pay-day-rule-dialog', - standalone: false, + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, PayDayRuleFormComponent], templateUrl: './pay-day-rule-dialog.component.html', styleUrls: ['./pay-day-rule-dialog.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-form/pay-day-rule-form.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-form/pay-day-rule-form.component.ts index 1686b7c6b..7b0658e9d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-form/pay-day-rule-form.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-form/pay-day-rule-form.component.ts @@ -1,12 +1,21 @@ import {Component, Input, OnInit} from '@angular/core'; -import {FormGroup, FormControl, Validators, FormArray, FormBuilder, AbstractControl, ValidationErrors} from '@angular/forms'; -import {MatTableDataSource} from '@angular/material/table'; -import {TranslateService} from '@ngx-translate/core'; +import {FormGroup, FormControl, Validators, FormArray, FormBuilder, AbstractControl, ValidationErrors, ReactiveFormsModule} from '@angular/forms'; +import {MatTableDataSource, MatTableModule} from '@angular/material/table'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import {secondsToHM} from '../../pay-rule-format.util'; +import {NgIf, NgFor} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatFormField, MatLabel, MatError, MatHint} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatSelect} from '@angular/material/select'; +import {MatOption} from '@angular/material/core'; +import {HhMmSecondsDirective} from '../../directives/hh-mm-seconds.directive'; @Component({ selector: 'app-pay-day-rule-form', - standalone: false, + imports: [NgIf, NgFor, ReactiveFormsModule, TranslatePipe, MatIcon, MatIconButton, MatFormField, MatLabel, MatError, MatHint, MatInput, MatTooltip, MatSelect, MatOption, MatTableModule, HhMmSecondsDirective], templateUrl: './pay-day-rule-form.component.html', styleUrls: ['./pay-day-rule-form.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-list/pay-day-rule-list.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-list/pay-day-rule-list.component.ts index 739c7c9d2..4d34eeb5d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-list/pay-day-rule-list.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-day-rule-list/pay-day-rule-list.component.ts @@ -1,11 +1,16 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { secondsToHM } from '../../pay-rule-format.util'; +import {NgIf} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatTableModule} from '@angular/material/table'; @Component({ selector: 'app-pay-day-rule-list', - standalone: false, + imports: [NgIf, TranslatePipe, MatIcon, MatIconButton, MatTooltip, MatTableModule], templateUrl: './pay-day-rule-list.component.html', styleUrls: ['./pay-day-rule-list.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-container/pay-rule-sets-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-container/pay-rule-sets-container.component.ts index a8de5e493..b94728ef0 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-container/pay-rule-sets-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-container/pay-rule-sets-container.component.ts @@ -10,14 +10,15 @@ import {PayRuleSetsViewModalComponent} from '../pay-rule-sets-view-modal/pay-rul import {TimePlanningPnPayRuleSetsService} from '../../../../services'; import {Subscription} from 'rxjs'; import {ToastrService} from 'ngx-toastr'; -import {TranslateService} from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; +import {PayRuleSetsTableComponent} from '../pay-rule-sets-table/pay-rule-sets-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-pay-rule-sets-container', + imports: [TranslatePipe, PayRuleSetsTableComponent], templateUrl: './pay-rule-sets-container.component.html', styleUrls: ['./pay-rule-sets-container.component.scss'], - standalone: false, }) export class PayRuleSetsContainerComponent implements OnInit, OnDestroy { payRuleSets: PayRuleSetSimpleModel[] = []; diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-create-modal/pay-rule-sets-create-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-create-modal/pay-rule-sets-create-modal.component.ts index ce665269d..105621e95 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-create-modal/pay-rule-sets-create-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-create-modal/pay-rule-sets-create-modal.component.ts @@ -1,18 +1,25 @@ import { Component, Inject, OnInit, Optional } from '@angular/core'; -import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms'; +import {FormBuilder, FormGroup, Validators, FormArray, FormsModule, ReactiveFormsModule} from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { MatDialog } from '@angular/material/dialog'; +import {MatDialog, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { TimePlanningPnPayRuleSetsService } from '../../../../services'; import { PayRuleSetCreateModel, PAY_RULE_SET_PRESETS, PayRuleSetPreset } from '../../../../models'; import { PayDayRuleDialogComponent, PayDayRuleDialogData } from '../pay-day-rule-dialog/pay-day-rule-dialog.component'; import { DayTypeRuleDialogComponent, DayTypeRuleDialogData } from '../day-type-rule-dialog/day-type-rule-dialog.component'; import { formatTierChain, formatTimeBands } from '../../pay-rule-format.util'; +import {NgIf, NgFor} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {PayDayRuleListComponent} from '../pay-day-rule-list/pay-day-rule-list.component'; +import {DayTypeRuleListComponent} from '../day-type-rule-list/day-type-rule-list.component'; @Component({ selector: 'app-pay-rule-sets-create-modal', - standalone: false, + imports: [NgIf, NgFor, FormsModule, ReactiveFormsModule, TranslatePipe, MatIcon, MatFormField, MatLabel, MatError, MatInput, MtxSelect, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, PayDayRuleListComponent, DayTypeRuleListComponent], templateUrl: './pay-rule-sets-create-modal.component.html', styleUrls: ['./pay-rule-sets-create-modal.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-delete-modal/pay-rule-sets-delete-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-delete-modal/pay-rule-sets-delete-modal.component.ts index 401dd55a1..c4045220d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-delete-modal/pay-rule-sets-delete-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-delete-modal/pay-rule-sets-delete-modal.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit, inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { PayRuleSetModel } from '../../../../models'; import { TimePlanningPnPayRuleSetsService } from '../../../../services'; @Component({ selector: 'app-pay-rule-sets-delete-modal', + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './pay-rule-sets-delete-modal.component.html', styleUrls: ['./pay-rule-sets-delete-modal.component.scss'], - standalone: false }) export class PayRuleSetsDeleteModalComponent implements OnInit { private payRuleSetsService = inject(TimePlanningPnPayRuleSetsService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-edit-modal/pay-rule-sets-edit-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-edit-modal/pay-rule-sets-edit-modal.component.ts index c0acafed1..0627eb4e2 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-edit-modal/pay-rule-sets-edit-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-edit-modal/pay-rule-sets-edit-modal.component.ts @@ -1,15 +1,21 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { FormBuilder, FormGroup, Validators, FormArray } from '@angular/forms'; +import {FormBuilder, FormGroup, Validators, FormArray, ReactiveFormsModule} from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { MatDialog } from '@angular/material/dialog'; +import {MatDialog, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose} from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; -import { TranslateService } from '@ngx-translate/core'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { TimePlanningPnPayRuleSetsService } from '../../../../services'; import { PayRuleSetUpdateModel, PayRuleSetModel, PAY_RULE_SET_PRESETS, PayRuleSetPreset } from '../../../../models'; import { PayDayRuleDialogComponent, PayDayRuleDialogData } from '../pay-day-rule-dialog/pay-day-rule-dialog.component'; import { DayTypeRuleDialogComponent, DayTypeRuleDialogData } from '../day-type-rule-dialog/day-type-rule-dialog.component'; import { formatTierChain, formatTimeBands } from '../../pay-rule-format.util'; import { isLockedPresetName, normalizePayRuleSetName } from '../../pay-rule-lock.util'; +import {NgIf, NgFor} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatFormField, MatLabel, MatError} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {PayDayRuleListComponent} from '../pay-day-rule-list/pay-day-rule-list.component'; +import {DayTypeRuleListComponent} from '../day-type-rule-list/day-type-rule-list.component'; export interface PayRuleSetsEditModalData { payRuleSetId: number; @@ -17,7 +23,7 @@ export interface PayRuleSetsEditModalData { @Component({ selector: 'app-pay-rule-sets-edit-modal', - standalone: false, + imports: [NgIf, NgFor, ReactiveFormsModule, TranslatePipe, MatIcon, MatFormField, MatLabel, MatError, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, PayDayRuleListComponent, DayTypeRuleListComponent], templateUrl: './pay-rule-sets-edit-modal.component.html', styleUrls: ['./pay-rule-sets-edit-modal.component.scss'] }) diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.html b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.html index 85fb3bb8a..294cb6b4a 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.html +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.html @@ -23,7 +23,7 @@
- diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.ts index 5935796d8..18fe72117 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/components/pay-rule-sets-table/pay-rule-sets-table.component.ts @@ -1,17 +1,22 @@ import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { MtxGridColumn } from '@ng-matero/extensions/grid'; -import { TranslateService } from '@ngx-translate/core'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { Store } from '@ngrx/store'; import { selectCurrentUserIsAdmin } from 'src/app/state'; import { PayRuleSetSimpleModel } from '../../../../models'; import { isLockedPresetName } from '../../pay-rule-lock.util'; +import {NgIf, AsyncPipe} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; @Component({ selector: 'app-pay-rule-sets-table', + imports: [NgIf, AsyncPipe, TranslatePipe, MatIcon, MatIconButton, MtxGrid, MatTooltip, MatMenu, MatMenuItem, MatMenuTrigger], templateUrl: './pay-rule-sets-table.component.html', styleUrls: ['./pay-rule-sets-table.component.scss'], - standalone: false }) export class PayRuleSetsTableComponent implements OnInit { private dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/directives/hh-mm-seconds.directive.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/directives/hh-mm-seconds.directive.ts index db01f51a9..b1f18ccf0 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/directives/hh-mm-seconds.directive.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/directives/hh-mm-seconds.directive.ts @@ -23,7 +23,6 @@ import {parseHhMmToSeconds, secondsToHhMmInput} from '../pay-rule-format.util'; */ @Directive({ selector: 'input[appHhMmSeconds]', - standalone: false, providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/pay-rule-sets.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/pay-rule-sets.module.ts index 3acbd6809..d981767f8 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/pay-rule-sets.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/pay-rule-sets/pay-rule-sets.module.ts @@ -1,9 +1,4 @@ -import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {RouterModule} from '@angular/router'; -import {TranslateModule} from '@ngx-translate/core'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; import {PayRuleSetsRouting} from './pay-rule-sets.routing'; import { PayRuleSetsContainerComponent, @@ -19,42 +14,12 @@ import { } from './components'; import {HhMmSecondsDirective} from './directives/hh-mm-seconds.directive'; import {TimePlanningPnPayRuleSetsService} from '../../services'; -import {MtxGridModule} from '@ng-matero/extensions/grid'; -import {MtxSelectModule} from '@ng-matero/extensions/select'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import {MatButtonModule} from '@angular/material/button'; -import {MatIconModule} from '@angular/material/icon'; import {MatDialogModule} from '@angular/material/dialog'; -import {MatTooltipModule} from '@angular/material/tooltip'; -import {MatSelectModule} from '@angular/material/select'; -import {MatMenuModule} from '@angular/material/menu'; -import {MatTableModule} from '@angular/material/table'; -import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; @NgModule({ imports: [ - CommonModule, - TranslateModule, - FormsModule, - EformSharedModule, - RouterModule, - ReactiveFormsModule, PayRuleSetsRouting, - MtxGridModule, - MtxSelectModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, MatDialogModule, - MatTooltipModule, - MatSelectModule, - MatMenuModule, - MatTableModule, - NgxMaterialTimepickerModule, - ], - declarations: [ PayRuleSetsContainerComponent, PayRuleSetsTableComponent, PayRuleSetsDeleteModalComponent, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-create-modal/registration-devices-create-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-create-modal/registration-devices-create-modal.component.ts index 9be83cb3b..cb7dce104 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-create-modal/registration-devices-create-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-create-modal/registration-devices-create-modal.component.ts @@ -1,16 +1,20 @@ import {Component, OnInit, inject } from '@angular/core'; -import {MatDialogRef} from '@angular/material/dialog'; +import {MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {TimePlanningPnRegistrationDevicesService} from '../../../../../services/time-planning-pn-registration-devices.service'; import {TimePlanningRegistrationDeviceModel} from '../../../../../../../modules/time-planning-pn/models'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-registration-devices-create-modal', + imports: [FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './registration-devices-create-modal.component.html' // styleUrls: ['./registration-devices-create.component.scss'] , - standalone: false }) export class RegistrationDevicesCreateModalComponent implements OnInit { private registrationDevicesService = inject(TimePlanningPnRegistrationDevicesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-delete-modal/registration-devices-delete-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-delete-modal/registration-devices-delete-modal.component.ts index 66a7f1444..77bcfaeaa 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-delete-modal/registration-devices-delete-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-delete-modal/registration-devices-delete-modal.component.ts @@ -1,16 +1,17 @@ import {Component, OnInit, inject } from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {TimePlanningPnRegistrationDevicesService} from 'src/app/plugins/modules/time-planning-pn/services'; import {TimePlanningRegistrationDeviceModel} from 'src/app/plugins/modules/time-planning-pn/models'; +import {TranslatePipe} from '@ngx-translate/core'; @Component({ selector: 'app-registration-devices-delete-modal', + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './registration-devices-delete-modal.component.html' // styleUrls: ['./registration-devices-delete.component.scss'] , - standalone: false }) export class RegistrationDevicesDeleteModalComponent implements OnInit { private registrationDevicesService = inject(TimePlanningPnRegistrationDevicesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-edit-modal/registration-devices-edit-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-edit-modal/registration-devices-edit-modal.component.ts index 17342d5f5..003667cdd 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-edit-modal/registration-devices-edit-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-edit-modal/registration-devices-edit-modal.component.ts @@ -1,16 +1,20 @@ import {Component, OnInit, inject } from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {TimePlanningPnRegistrationDevicesService} from 'src/app/plugins/modules/time-planning-pn/services'; import {TimePlanningRegistrationDeviceModel} from 'src/app/plugins/modules/time-planning-pn/models'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @Component({ selector: 'app-registration-devices-edit-modal', + imports: [FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './registration-devices-edit-modal.component.html' // styleUrls: ['./registration-devices-create.component.scss'] , - standalone: false }) export class RegistrationDevicesEditModalComponent implements OnInit { private registrationDevicesService = inject(TimePlanningPnRegistrationDevicesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-otp-code/registration-devices-otp-code.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-otp-code/registration-devices-otp-code.component.ts index 1e4675a9a..4dcaf9814 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-otp-code/registration-devices-otp-code.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-actions/registration-devices-otp-code/registration-devices-otp-code.component.ts @@ -1,16 +1,17 @@ import {Component, OnInit, inject } from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {TimePlanningRegistrationDeviceModel} from '../../../../../models'; import {TimePlanningPnRegistrationDevicesService} from '../../../../../services'; +import {TranslatePipe} from '@ngx-translate/core'; @Component({ selector: 'app-registration-devices-otp-code', + imports: [TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './registration-devices-otp-code.component.html', - standalone: false }) export class RegistrationDevicesOtpCodeComponent implements OnInit { private timePlanningPnRegistrationDevicesService = inject(TimePlanningPnRegistrationDevicesService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-container/registration-devices-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-container/registration-devices-container.component.ts index faf4b3d7f..d75e91293 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-container/registration-devices-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-container/registration-devices-container.component.ts @@ -11,11 +11,14 @@ import {dialogConfigHelper} from 'src/app/common/helpers'; import { RegistrationDevicesCreateModalComponent } from '../index'; +import {TranslatePipe} from '@ngx-translate/core'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; +import {RegistrationDevicesTableComponent} from '../registration-devices-table/registration-devices-table.component'; @Component({ selector: 'app-registration-devices-container', + imports: [TranslatePipe, EformNewSubheaderComponent, RegistrationDevicesTableComponent], templateUrl: './registration-devices-container.component.html', - standalone: false }) export class RegistrationDevicesContainerComponent implements OnInit { public dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.html b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.html index e7e9d71b7..c55f70d6e 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.html +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.html @@ -39,7 +39,7 @@
- diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.ts index 9e033836b..96fd15ecf 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/components/registration-devices-table/registration-devices-table.component.ts @@ -1,8 +1,8 @@ import {Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; -import {MtxGridColumn} from '@ng-matero/extensions/grid'; -import {TranslateService} from '@ngx-translate/core'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import {TimePlanningRegistrationDeviceModel} from '../../../../../../modules/time-planning-pn/models'; import {Subscription} from 'rxjs'; import { @@ -13,11 +13,16 @@ import { import {MatDialog} from '@angular/material/dialog'; import {dialogConfigHelper} from 'src/app/common/helpers'; import {Overlay} from '@angular/cdk/overlay'; +import {NgIf} from '@angular/common'; +import {MatIcon} from '@angular/material/icon'; +import {MatIconButton} from '@angular/material/button'; +import {MatTooltip} from '@angular/material/tooltip'; +import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; @Component({ selector: 'app-registration-devices-table', + imports: [NgIf, TranslatePipe, MatIcon, MatIconButton, MtxGrid, MatTooltip, MatMenu, MatMenuItem, MatMenuTrigger], templateUrl: './registration-devices-table.component.html', - standalone: false }) export class RegistrationDevicesTableComponent implements OnInit { private dialog = inject(MatDialog); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/registration-devices.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/registration-devices.module.ts index 33af1b4fe..f75858cf2 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/registration-devices.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/registration-devices/registration-devices.module.ts @@ -1,4 +1,5 @@ import {NgModule} from '@angular/core'; +import {MatDialogModule} from '@angular/material/dialog'; import { RegistrationDevicesContainerComponent, RegistrationDevicesCreateModalComponent, @@ -6,46 +7,14 @@ import { RegistrationDevicesOtpCodeComponent, RegistrationDevicesTableComponent } from './components'; -import {AsyncPipe, NgIf} from '@angular/common'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; -import {MatButton, MatIconButton} from '@angular/material/button'; -import {TranslateModule} from '@ngx-translate/core'; import { RegistrationDevicesRouting } from './registration-devices.routing'; -import {MtxGrid} from '@ng-matero/extensions/grid'; -import {MatDialogActions, MatDialogContent, MatDialogTitle} from '@angular/material/dialog'; -import {MatIcon} from '@angular/material/icon'; -import {MatTooltip} from '@angular/material/tooltip'; -import {FormsModule} from '@angular/forms'; -import {MatFormField, MatLabel} from '@angular/material/form-field'; -import {MatInput} from '@angular/material/input'; -import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu"; @NgModule({ imports: [ - AsyncPipe, - EformSharedModule, - MatButton, - NgIf, - TranslateModule, + MatDialogModule, RegistrationDevicesRouting, - MtxGrid, - MatDialogTitle, - MatDialogContent, - MatDialogActions, - MatIcon, - MatIconButton, - MatTooltip, - FormsModule, - MatFormField, - MatInput, - MatLabel, - MatMenu, - MatMenuItem, - MatMenuTrigger - ], - declarations: [ RegistrationDevicesTableComponent, RegistrationDevicesContainerComponent, RegistrationDevicesCreateModalComponent, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/components/request-history-page/request-history-page.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/components/request-history-page/request-history-page.component.ts index 35e9bf69e..58d85888d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/components/request-history-page/request-history-page.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/components/request-history-page/request-history-page.component.ts @@ -1,13 +1,20 @@ import { Component, OnInit, OnDestroy, inject, LOCALE_ID } from '@angular/core'; -import { formatDate } from '@angular/common'; +import {formatDate, NgClass} from '@angular/common'; import { TimePlanningPnRequestHistoryService, RequestHistoryQueryParams } from '../../../../services'; import { Subscription, forkJoin, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; -import { MtxGridColumn } from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; +import {MtxGridColumn, MtxGrid} from '@ng-matero/extensions/grid'; import { AutoUnsubscribe } from 'ngx-auto-unsubscribe'; import { ToastrService } from 'ngx-toastr'; import { messages } from '../../../../consts/messages'; +import {FormsModule} from '@angular/forms'; +import {MatButton} from '@angular/material/button'; +import {MatCard} from '@angular/material/card'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatChip} from '@angular/material/chips'; export interface RequestHistoryRow { type: string; @@ -26,9 +33,9 @@ export interface RequestHistoryRow { @AutoUnsubscribe() @Component({ selector: 'app-request-history-page', + imports: [NgClass, FormsModule, TranslatePipe, MatButton, MatCard, MatFormField, MatLabel, MatInput, MtxSelect, MtxGrid, MatChip], templateUrl: './request-history-page.component.html', styleUrls: ['./request-history-page.component.scss'], - standalone: false }) export class RequestHistoryPageComponent implements OnInit, OnDestroy { private requestHistoryService = inject(TimePlanningPnRequestHistoryService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/request-history.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/request-history.module.ts index d59c06758..d3d83ad02 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/request-history.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/request-history/request-history.module.ts @@ -1,37 +1,14 @@ import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { EformSharedModule } from 'src/app/common/modules/eform-shared/eform-shared.module'; -import { MatButton } from '@angular/material/button'; -import { TranslateModule } from '@ngx-translate/core'; +import {MatDialogModule} from '@angular/material/dialog'; import { RequestHistoryRouting } from './request-history.routing'; -import { MtxGrid } from '@ng-matero/extensions/grid'; -import { FormsModule } from '@angular/forms'; -import { MatFormField, MatLabel } from '@angular/material/form-field'; -import { MatInput } from '@angular/material/input'; -import { MatCard } from '@angular/material/card'; -import { MatChip } from '@angular/material/chips'; -import { MtxSelectModule } from '@ng-matero/extensions/select'; import { RequestHistoryPageComponent } from './components'; @NgModule({ imports: [ - CommonModule, - EformSharedModule, - MatButton, - TranslateModule, + MatDialogModule, RequestHistoryRouting, - MtxGrid, - FormsModule, - MatFormField, - MatInput, - MatLabel, - MatCard, - MatChip, - MtxSelectModule - ], - declarations: [ RequestHistoryPageComponent ], providers: [], diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/mobile-working-hours/mobile-working-hours.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/mobile-working-hours/mobile-working-hours.component.ts index ad37c1bbd..22af2a44e 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/mobile-working-hours/mobile-working-hours.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/mobile-working-hours/mobile-working-hours.component.ts @@ -7,12 +7,13 @@ import {format} from 'date-fns'; import {PARSING_DATE_FORMAT} from 'src/app/common/const'; import {take} from "rxjs"; import {TitleService} from "src/app/common/services"; -import {TranslateService} from "@ngx-translate/core"; +import {TranslateService, TranslatePipe} from "@ngx-translate/core"; +import {MatCard} from '@angular/material/card'; @Component({ selector: 'app-mobile-working-hours', + imports: [TranslatePipe, MatCard], templateUrl: './mobile-working-hours.component.html', styleUrls: ['./mobile-working-hours.component.scss'], - standalone: false }) export class MobileWorkingHoursComponent implements OnInit, OnDestroy { private titleService = inject(TitleService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-all-update-modal/working-hours-comment-office-all-update-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-all-update-modal/working-hours-comment-office-all-update-modal.component.ts index 78d7b9518..7b75d2681 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-all-update-modal/working-hours-comment-office-all-update-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-all-update-modal/working-hours-comment-office-all-update-modal.component.ts @@ -2,15 +2,19 @@ import {Component, OnDestroy, OnInit, inject } from '@angular/core'; import {AutoUnsubscribe} from 'ngx-auto-unsubscribe'; -import {FormGroup} from '@angular/forms'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {FormGroup, FormsModule} from '@angular/forms'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; +import {DatePipe} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @AutoUnsubscribe() @Component({ selector: 'app-working-hours-comment-office-all-update-modal', + imports: [DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './working-hours-comment-office-all-update-modal.component.html', styleUrls: ['./working-hours-comment-office-all-update-modal.component.scss'], - standalone: false }) export class WorkingHoursCommentOfficeAllUpdateModalComponent implements OnInit, OnDestroy { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-update-modal/working-hours-comment-office-update-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-update-modal/working-hours-comment-office-update-modal.component.ts index 74649593b..c902947ff 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-update-modal/working-hours-comment-office-update-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-comment-office-update-modal/working-hours-comment-office-update-modal.component.ts @@ -2,15 +2,19 @@ import {Component, OnDestroy, OnInit, inject } from '@angular/core'; import {AutoUnsubscribe} from 'ngx-auto-unsubscribe'; -import {FormGroup} from '@angular/forms'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {FormGroup, FormsModule} from '@angular/forms'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; +import {DatePipe} from '@angular/common'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; @AutoUnsubscribe() @Component({ selector: 'app-working-hours-comment-office-update-modal', + imports: [DatePipe, FormsModule, TranslatePipe, MatFormField, MatLabel, MatInput, MatDialogTitle, MatDialogContent, MatDialogActions], templateUrl: './working-hours-comment-office-update-modal.component.html', styleUrls: ['./working-hours-comment-office-update-modal.component.scss'], - standalone: false }) export class WorkingHoursCommentOfficeUpdateModalComponent implements OnInit, OnDestroy { diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-upload-modal/working-hours-upload-modal.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-upload-modal/working-hours-upload-modal.component.ts index 960746bf9..9bf3155ab 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-upload-modal/working-hours-upload-modal.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-actions/working-hours-upload-modal/working-hours-upload-modal.component.ts @@ -1,20 +1,21 @@ import {Component, OnInit, inject } from '@angular/core'; -import {TranslateService} from '@ngx-translate/core'; -import {FileUploader} from 'ng2-file-upload'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; +import {FileUploader, FileUploadModule} from 'ng2-file-upload'; import {ToastrService} from 'ngx-toastr'; import {TemplateDto} from 'src/app/common/models/dto'; import {AuthStateService} from 'src/app/common/store'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions} from '@angular/material/dialog'; import {selectBearerToken} from 'src/app/state/auth/auth.selector'; import {Store} from '@ngrx/store'; +import {NgIf, NgStyle} from '@angular/common'; @Component({ selector: 'app-working-hours-upload-modal', + imports: [NgIf, NgStyle, TranslatePipe, MatDialogTitle, MatDialogContent, MatDialogActions, FileUploadModule], templateUrl: './working-hours-upload-modal.component.html', styleUrls: ['./working-hours-upload-modal.component.scss'], - standalone: false }) export class WorkingHoursUploadModalComponent implements OnInit { private toastrService = inject(ToastrService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-container/working-hours-container.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-container/working-hours-container.component.ts index 62a766f40..3e4f81bd1 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-container/working-hours-container.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-container/working-hours-container.component.ts @@ -10,13 +10,15 @@ import { TimePlanningPnWorkingHoursService, } from '../../../../services'; import {WorkingHourRequestModel, WorkingHourModel} from '../../../../models'; +import {EformNewSubheaderComponent} from 'src/app/common/modules/eform-shared/components'; +import {WorkingHoursTableComponent} from '../working-hours-table/working-hours-table.component'; @AutoUnsubscribe() @Component({ selector: 'app-working-hours-container', + imports: [EformNewSubheaderComponent, WorkingHoursTableComponent], templateUrl: './working-hours-container.component.html', styleUrls: ['./working-hours-container.component.scss'], - standalone: false }) export class WorkingHoursContainerComponent implements OnInit, OnDestroy { private workingHoursService = inject(TimePlanningPnWorkingHoursService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-header/working-hours-header.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-header/working-hours-header.component.ts index c44e828f2..184c86bb7 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-header/working-hours-header.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-header/working-hours-header.component.ts @@ -9,10 +9,10 @@ import {saveAs} from 'file-saver'; import {ToastrService} from 'ngx-toastr'; import {TimePlanningPnWorkingHoursService} from '../../../../services'; import {EMPTY, Subscription} from 'rxjs'; -import {MatIconRegistry} from '@angular/material/icon'; +import {MatIconRegistry, MatIcon} from '@angular/material/icon'; import {DomSanitizer} from '@angular/platform-browser'; import {catchError} from 'rxjs/operators'; -import {MatDatepickerInputEvent} from '@angular/material/datepicker'; +import {MatDatepickerInputEvent, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate} from '@angular/material/datepicker'; import {dialogConfigHelper} from 'src/app/common/helpers'; import {MatDialog} from '@angular/material/dialog'; import {Overlay} from '@angular/cdk/overlay'; @@ -22,12 +22,18 @@ import { import { WorkingHourRequestModel } from '../../../../models'; +import {NgIf} from '@angular/common'; +import {FormsModule} from '@angular/forms'; +import {TranslatePipe} from '@ngx-translate/core'; +import {MatFormField, MatLabel, MatPrefix} from '@angular/material/form-field'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {MatTooltip} from '@angular/material/tooltip'; @Component({ selector: 'app-working-hours-header', + imports: [NgIf, FormsModule, TranslatePipe, MatIcon, MatFormField, MatLabel, MatPrefix, MtxSelect, MatTooltip, MatDatepickerToggle, MatDateRangeInput, MatDateRangePicker, MatStartDate, MatEndDate], templateUrl: './working-hours-header.component.html', styleUrls: ['./working-hours-header.component.scss'], - standalone: false }) export class WorkingHoursHeaderComponent implements OnInit { private toastrService = inject(ToastrService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.html b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.html index d650b1902..4d1067ae8 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.html +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.html @@ -56,7 +56,7 @@ - + diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.ts index 3b15278ac..87b9bc09f 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/components/working-hours-table/working-hours-table.component.ts @@ -8,11 +8,11 @@ import { SimpleChanges, TemplateRef, ViewChild, ViewEncapsulation, inject } from '@angular/core'; -import {AbstractControl, FormArray, FormControl, FormGroup} from '@angular/forms'; +import {AbstractControl, FormArray, FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {Subscription} from 'rxjs'; import {SiteDto} from 'src/app/common/models'; -import {MtxGridColumn, MtxGridRowClassFormatter} from '@ng-matero/extensions/grid'; -import {TranslateService} from '@ngx-translate/core'; +import {MtxGridColumn, MtxGridRowClassFormatter, MtxGrid} from '@ng-matero/extensions/grid'; +import {TranslateService, TranslatePipe} from '@ngx-translate/core'; import { DaysOfWeekEnum, FULL_DATE_FORMAT, HOURS_PICKER_ARRAY, @@ -32,14 +32,18 @@ import { WorkingHourModel, WorkingHourRequestModel } from '../../../../models'; +import {MatFormField} from '@angular/material/form-field'; +import {MatInput} from '@angular/material/input'; +import {MtxSelect} from '@ng-matero/extensions/select'; +import {WorkingHoursHeaderComponent} from '../working-hours-header/working-hours-header.component'; @AutoUnsubscribe() @Component({ selector: 'app-working-hours-table', + imports: [ReactiveFormsModule, TranslatePipe, MatFormField, MatInput, MtxSelect, MtxGrid, WorkingHoursHeaderComponent], templateUrl: './working-hours-table.component.html', styleUrls: ['./working-hours-table.component.css'], encapsulation: ViewEncapsulation.None, - standalone: false }) export class WorkingHoursTableComponent implements OnInit, OnChanges, OnDestroy { private translateService = inject(TranslateService); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/working-hours.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/working-hours.module.ts index 71bc02dd8..6399dcd21 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/working-hours.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/modules/working-hours/working-hours.module.ts @@ -1,9 +1,4 @@ -import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {RouterModule} from '@angular/router'; -import {TranslateModule} from '@ngx-translate/core'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; import {WorkingHoursRouting} from './working-hours.routing'; import { WorkingHoursContainerComponent, @@ -14,44 +9,12 @@ import { WorkingHoursUploadModalComponent, MobileWorkingHoursComponent, } from './components'; -import {MtxGridModule} from '@ng-matero/extensions/grid'; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import {MtxSelectModule} from '@ng-matero/extensions/select'; -import {MatButtonModule} from '@angular/material/button'; -import {MatIconModule} from '@angular/material/icon'; -import {MatTableModule} from '@angular/material/table'; import {MatDialogModule} from '@angular/material/dialog'; -import {MatDatepickerModule} from '@angular/material/datepicker'; -import {MatTooltipModule} from '@angular/material/tooltip'; -import {FileUploadModule} from 'ng2-file-upload'; -import {MatCard} from "@angular/material/card"; -import {MatPasswordStrengthModule} from "@angular-material-extensions/password-strength"; @NgModule({ - imports: [ - CommonModule, - TranslateModule, - FormsModule, - EformSharedModule, - RouterModule, - ReactiveFormsModule, - WorkingHoursRouting, - MtxGridModule, - MatFormFieldModule, - MatInputModule, - MtxSelectModule, - MatButtonModule, - MatIconModule, - MatTableModule, - MatDialogModule, - MatDatepickerModule, - MatTooltipModule, - FileUploadModule, - MatCard, - MatPasswordStrengthModule, - ], - declarations: [ + imports: [ + WorkingHoursRouting, + MatDialogModule, MobileWorkingHoursComponent, WorkingHoursContainerComponent, WorkingHoursHeaderComponent, diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-absence-requests.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-absence-requests.service.spec.ts index 9324a96b4..208c1add9 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-absence-requests.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-absence-requests.service.spec.ts @@ -1,16 +1,18 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnAbsenceRequestsService } from './time-planning-pn-absence-requests.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; import { AbsenceRequestModel, AbsenceRequestDecisionModel } from '../models'; describe('TimePlanningPnAbsenceRequestsService', () => { let service: TimePlanningPnAbsenceRequestsService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - post: jest.fn(), - get: jest.fn(), + post: vi.fn(), + get: vi.fn(), } as any; service = new TimePlanningPnAbsenceRequestsService(mockApiBaseService); @@ -21,15 +23,13 @@ describe('TimePlanningPnAbsenceRequestsService', () => { }); describe('getInbox', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const managerSdkSitId = 1; const mockResponse = { success: true, model: [] as AbsenceRequestModel[] }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getInbox(managerSdkSitId).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getInbox(managerSdkSitId)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/absence-requests/inbox', @@ -37,28 +37,24 @@ describe('TimePlanningPnAbsenceRequestsService', () => { ); }); - it('should handle empty response', (done) => { + it('should handle empty response', async () => { const managerSdkSitId = 1; const mockResponse = { success: true, model: [] as AbsenceRequestModel[] }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getInbox(managerSdkSitId).subscribe(result => { - expect(result.model).toEqual([]); - done(); - }); + const result = await firstValueFrom(service.getInbox(managerSdkSitId)); + expect(result.model).toEqual([]); }); }); describe('getMine', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const requestedBySdkSitId = 2; const mockResponse = { success: true, model: [] as AbsenceRequestModel[] }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getMine(requestedBySdkSitId).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getMine(requestedBySdkSitId)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/absence-requests/mine', @@ -68,7 +64,7 @@ describe('TimePlanningPnAbsenceRequestsService', () => { }); describe('approve', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const requestId = 123; const decisionModel: AbsenceRequestDecisionModel = { managerSdkSitId: 1, @@ -77,10 +73,8 @@ describe('TimePlanningPnAbsenceRequestsService', () => { const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.approve(requestId, decisionModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.approve(requestId, decisionModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith( 'api/time-planning-pn/absence-requests/123/approve', @@ -106,7 +100,7 @@ describe('TimePlanningPnAbsenceRequestsService', () => { }); describe('reject', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const requestId = 789; const decisionModel: AbsenceRequestDecisionModel = { managerSdkSitId: 1, @@ -115,10 +109,8 @@ describe('TimePlanningPnAbsenceRequestsService', () => { const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.reject(requestId, decisionModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.reject(requestId, decisionModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith( 'api/time-planning-pn/absence-requests/789/reject', @@ -126,7 +118,7 @@ describe('TimePlanningPnAbsenceRequestsService', () => { ); }); - it('should handle rejection without comment', (done) => { + it('should handle rejection without comment', async () => { const requestId = 111; const decisionModel: AbsenceRequestDecisionModel = { managerSdkSitId: 1 @@ -134,10 +126,8 @@ describe('TimePlanningPnAbsenceRequestsService', () => { const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.reject(requestId, decisionModel).subscribe(result => { - expect(result.success).toBe(true); - done(); - }); + const result = await firstValueFrom(service.reject(requestId, decisionModel)); + expect(result.success).toBe(true); expect(mockApiBaseService.post).toHaveBeenCalledWith( 'api/time-planning-pn/absence-requests/111/reject', diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-break-policies.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-break-policies.service.spec.ts index 1f24cb280..25946ec9a 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-break-policies.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-break-policies.service.spec.ts @@ -1,17 +1,19 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnBreakPoliciesService } from './time-planning-pn-break-policies.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnBreakPoliciesService', () => { let service: TimePlanningPnBreakPoliciesService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), } as any; service = new TimePlanningPnBreakPoliciesService(mockApiBaseService); @@ -22,7 +24,7 @@ describe('TimePlanningPnBreakPoliciesService', () => { }); describe('getBreakPolicies', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const mockRequest = { offset: 0, pageSize: 10, @@ -30,10 +32,8 @@ describe('TimePlanningPnBreakPoliciesService', () => { const mockResponse = { success: true, model: { total: 0, breakPolicies: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getBreakPolicies(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getBreakPolicies(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies', @@ -41,7 +41,7 @@ describe('TimePlanningPnBreakPoliciesService', () => { ); }); - it('should handle pagination', (done) => { + it('should handle pagination', async () => { const mockRequest = { offset: 20, pageSize: 50, @@ -49,10 +49,8 @@ describe('TimePlanningPnBreakPoliciesService', () => { const mockResponse = { success: true, model: { total: 100, breakPolicies: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getBreakPolicies(mockRequest).subscribe(result => { - expect(result.model.total).toEqual(100); - done(); - }); + const result = await firstValueFrom(service.getBreakPolicies(mockRequest)); + expect(result.model.total).toEqual(100); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies', @@ -62,7 +60,7 @@ describe('TimePlanningPnBreakPoliciesService', () => { }); describe('getBreakPolicy', () => { - it('should call apiBaseService.get with correct id', (done) => { + it('should call apiBaseService.get with correct id', async () => { const mockId = 123; const mockResponse = { success: true, @@ -70,10 +68,8 @@ describe('TimePlanningPnBreakPoliciesService', () => { }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getBreakPolicy(mockId).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getBreakPolicy(mockId)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies/123' @@ -82,7 +78,7 @@ describe('TimePlanningPnBreakPoliciesService', () => { }); describe('createBreakPolicy', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockModel = { name: 'New Policy', breakPolicyRules: [] @@ -90,10 +86,8 @@ describe('TimePlanningPnBreakPoliciesService', () => { const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.createBreakPolicy(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.createBreakPolicy(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies', @@ -103,7 +97,7 @@ describe('TimePlanningPnBreakPoliciesService', () => { }); describe('updateBreakPolicy', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockModel = { name: 'Updated Policy', breakPolicyRules: [] @@ -111,10 +105,8 @@ describe('TimePlanningPnBreakPoliciesService', () => { const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updateBreakPolicy(123, mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updateBreakPolicy(123, mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies/123', @@ -137,15 +129,13 @@ describe('TimePlanningPnBreakPoliciesService', () => { }); describe('deleteBreakPolicy', () => { - it('should call apiBaseService.delete with correct id', (done) => { + it('should call apiBaseService.delete with correct id', async () => { const mockId = 123; const mockResponse = { success: true }; mockApiBaseService.delete.mockReturnValue(of(mockResponse as any)); - service.deleteBreakPolicy(mockId).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.deleteBreakPolicy(mockId)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.delete).toHaveBeenCalledWith( 'api/time-planning-pn/break-policies/123' diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-day-type-rules.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-day-type-rules.service.spec.ts index 795f04f81..bc6829346 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-day-type-rules.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-day-type-rules.service.spec.ts @@ -1,17 +1,19 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnPayDayTypeRulesService } from './time-planning-pn-pay-day-type-rules.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPayDayTypeRulesService', () => { let service: TimePlanningPnPayDayTypeRulesService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), } as any; service = new TimePlanningPnPayDayTypeRulesService(mockApiBaseService); @@ -22,15 +24,13 @@ describe('TimePlanningPnPayDayTypeRulesService', () => { }); describe('getPayDayTypeRules', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const mockRequest = { offset: 0, pageSize: 10 }; const mockResponse = { success: true, model: { total: 0, payDayTypeRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayDayTypeRules(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayDayTypeRules(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-day-type-rules', @@ -38,14 +38,12 @@ describe('TimePlanningPnPayDayTypeRulesService', () => { ); }); - it('should include optional payRuleSetId filter', (done) => { + it('should include optional payRuleSetId filter', async () => { const mockRequest = { offset: 0, pageSize: 10, payRuleSetId: 5 }; const mockResponse = { success: true, model: { total: 0, payDayTypeRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayDayTypeRules(mockRequest).subscribe(result => { - done(); - }); + await firstValueFrom(service.getPayDayTypeRules(mockRequest)); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-day-type-rules', @@ -55,58 +53,50 @@ describe('TimePlanningPnPayDayTypeRulesService', () => { }); describe('getPayDayTypeRule', () => { - it('should call apiBaseService.get with correct id', (done) => { + it('should call apiBaseService.get with correct id', async () => { const mockResponse = { success: true, model: { id: 123, payRuleSetId: 1, dayType: 'Weekday' } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayDayTypeRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayDayTypeRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith('api/time-planning-pn/pay-day-type-rules/123'); }); }); describe('createPayDayTypeRule', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockModel = { payRuleSetId: 1, dayType: 'Weekday' }; const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.createPayDayTypeRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.createPayDayTypeRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith('api/time-planning-pn/pay-day-type-rules', mockModel); }); }); describe('updatePayDayTypeRule', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockModel = { id: 123, payRuleSetId: 1, dayType: 'Weekend' }; const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updatePayDayTypeRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updatePayDayTypeRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith('api/time-planning-pn/pay-day-type-rules/123', mockModel); }); }); describe('deletePayDayTypeRule', () => { - it('should call apiBaseService.delete with correct id', (done) => { + it('should call apiBaseService.delete with correct id', async () => { const mockResponse = { success: true }; mockApiBaseService.delete.mockReturnValue(of(mockResponse as any)); - service.deletePayDayTypeRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.deletePayDayTypeRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.delete).toHaveBeenCalledWith('api/time-planning-pn/pay-day-type-rules/123'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-rule-sets.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-rule-sets.service.spec.ts index 9a168dffd..e96e8063a 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-rule-sets.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-rule-sets.service.spec.ts @@ -1,17 +1,19 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnPayRuleSetsService } from './time-planning-pn-pay-rule-sets.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPayRuleSetsService', () => { let service: TimePlanningPnPayRuleSetsService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), } as any; service = new TimePlanningPnPayRuleSetsService(mockApiBaseService); @@ -22,15 +24,13 @@ describe('TimePlanningPnPayRuleSetsService', () => { }); describe('getPayRuleSets', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const mockRequest = { offset: 0, pageSize: 10 }; const mockResponse = { success: true, model: { total: 0, payRuleSets: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayRuleSets(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayRuleSets(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-rule-sets', @@ -40,58 +40,50 @@ describe('TimePlanningPnPayRuleSetsService', () => { }); describe('getPayRuleSet', () => { - it('should call apiBaseService.get with correct id', (done) => { + it('should call apiBaseService.get with correct id', async () => { const mockResponse = { success: true, model: { id: 123, name: 'Test', payDayRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayRuleSet(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayRuleSet(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith('api/time-planning-pn/pay-rule-sets/123'); }); }); describe('createPayRuleSet', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockModel = { name: 'New RuleSet', payDayRules: [], payDayTypeRules: [] }; const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.createPayRuleSet(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.createPayRuleSet(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith('api/time-planning-pn/pay-rule-sets', mockModel); }); }); describe('updatePayRuleSet', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockModel = { name: 'Updated', payDayRules: [], payDayTypeRules: [] }; const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updatePayRuleSet(123, mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updatePayRuleSet(123, mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith('api/time-planning-pn/pay-rule-sets/123', mockModel); }); }); describe('deletePayRuleSet', () => { - it('should call apiBaseService.delete with correct id', (done) => { + it('should call apiBaseService.delete with correct id', async () => { const mockResponse = { success: true }; mockApiBaseService.delete.mockReturnValue(of(mockResponse as any)); - service.deletePayRuleSet(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.deletePayRuleSet(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.delete).toHaveBeenCalledWith('api/time-planning-pn/pay-rule-sets/123'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-tier-rules.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-tier-rules.service.spec.ts index b29197913..7d2de6ad4 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-tier-rules.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-tier-rules.service.spec.ts @@ -1,17 +1,19 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnPayTierRulesService } from './time-planning-pn-pay-tier-rules.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPayTierRulesService', () => { let service: TimePlanningPnPayTierRulesService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), } as any; service = new TimePlanningPnPayTierRulesService(mockApiBaseService); @@ -22,15 +24,13 @@ describe('TimePlanningPnPayTierRulesService', () => { }); describe('getPayTierRules', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const mockRequest = { offset: 0, pageSize: 10 }; const mockResponse = { success: true, model: { total: 0, payTierRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTierRules(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayTierRules(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-tier-rules', @@ -38,14 +38,12 @@ describe('TimePlanningPnPayTierRulesService', () => { ); }); - it('should include optional payDayRuleId filter', (done) => { + it('should include optional payDayRuleId filter', async () => { const mockRequest = { offset: 0, pageSize: 10, payDayRuleId: 3 }; const mockResponse = { success: true, model: { total: 0, payTierRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTierRules(mockRequest).subscribe(result => { - done(); - }); + await firstValueFrom(service.getPayTierRules(mockRequest)); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-tier-rules', @@ -55,58 +53,50 @@ describe('TimePlanningPnPayTierRulesService', () => { }); describe('getPayTierRule', () => { - it('should call apiBaseService.get with correct id', (done) => { + it('should call apiBaseService.get with correct id', async () => { const mockResponse = { success: true, model: { id: 123, payDayRuleId: 1, order: 1, upToSeconds: 3600, payCode: 'CODE1' } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTierRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayTierRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith('api/time-planning-pn/pay-tier-rules/123'); }); }); describe('createPayTierRule', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockModel = { payDayRuleId: 1, order: 1, upToSeconds: 3600, payCode: 'CODE1' }; const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.createPayTierRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.createPayTierRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith('api/time-planning-pn/pay-tier-rules', mockModel); }); }); describe('updatePayTierRule', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockModel = { id: 123, payDayRuleId: 1, order: 2, upToSeconds: 7200, payCode: 'CODE2' }; const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updatePayTierRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updatePayTierRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith('api/time-planning-pn/pay-tier-rules/123', mockModel); }); }); describe('deletePayTierRule', () => { - it('should call apiBaseService.delete with correct id', (done) => { + it('should call apiBaseService.delete with correct id', async () => { const mockResponse = { success: true }; mockApiBaseService.delete.mockReturnValue(of(mockResponse as any)); - service.deletePayTierRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.deletePayTierRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.delete).toHaveBeenCalledWith('api/time-planning-pn/pay-tier-rules/123'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-time-band-rules.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-time-band-rules.service.spec.ts index 52e063f4c..6c0b794fd 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-time-band-rules.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-pay-time-band-rules.service.spec.ts @@ -1,17 +1,19 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnPayTimeBandRulesService } from './time-planning-pn-pay-time-band-rules.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPayTimeBandRulesService', () => { let service: TimePlanningPnPayTimeBandRulesService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), } as any; service = new TimePlanningPnPayTimeBandRulesService(mockApiBaseService); @@ -22,15 +24,13 @@ describe('TimePlanningPnPayTimeBandRulesService', () => { }); describe('getPayTimeBandRules', () => { - it('should call apiBaseService.get with correct parameters', (done) => { + it('should call apiBaseService.get with correct parameters', async () => { const mockRequest = { offset: 0, pageSize: 10 }; const mockResponse = { success: true, model: { total: 0, payTimeBandRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTimeBandRules(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayTimeBandRules(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-time-band-rules', @@ -38,14 +38,12 @@ describe('TimePlanningPnPayTimeBandRulesService', () => { ); }); - it('should include optional payDayTypeRuleId filter', (done) => { + it('should include optional payDayTypeRuleId filter', async () => { const mockRequest = { offset: 0, pageSize: 10, payDayTypeRuleId: 2 }; const mockResponse = { success: true, model: { total: 0, payTimeBandRules: [] } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTimeBandRules(mockRequest).subscribe(result => { - done(); - }); + await firstValueFrom(service.getPayTimeBandRules(mockRequest)); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/pay-time-band-rules', @@ -55,61 +53,53 @@ describe('TimePlanningPnPayTimeBandRulesService', () => { }); describe('getPayTimeBandRule', () => { - it('should call apiBaseService.get with correct id', (done) => { + it('should call apiBaseService.get with correct id', async () => { const mockResponse = { success: true, model: { id: 123, payDayTypeRuleId: 1, startSecondOfDay: 0, endSecondOfDay: 43200, payCode: 'DAY' } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.getPayTimeBandRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPayTimeBandRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith('api/time-planning-pn/pay-time-band-rules/123'); }); }); describe('createPayTimeBandRule', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockModel = { payDayTypeRuleId: 1, startSecondOfDay: 0, endSecondOfDay: 43200, payCode: 'DAY', priority: 1 }; const mockResponse = { success: true }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.createPayTimeBandRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.createPayTimeBandRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith('api/time-planning-pn/pay-time-band-rules', mockModel); }); }); describe('updatePayTimeBandRule', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockModel = { id: 123, payDayTypeRuleId: 1, startSecondOfDay: 43200, endSecondOfDay: 86400, payCode: 'NIGHT', priority: 1 }; const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updatePayTimeBandRule(mockModel).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updatePayTimeBandRule(mockModel)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith('api/time-planning-pn/pay-time-band-rules/123', mockModel); }); }); describe('deletePayTimeBandRule', () => { - it('should call apiBaseService.delete with correct id', (done) => { + it('should call apiBaseService.delete with correct id', async () => { const mockResponse = { success: true }; mockApiBaseService.delete.mockReturnValue(of(mockResponse as any)); - service.deletePayTimeBandRule(123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.deletePayTimeBandRule(123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.delete).toHaveBeenCalledWith('api/time-planning-pn/pay-time-band-rules/123'); }); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-payroll-export.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-payroll-export.service.spec.ts index e4235c0ec..9cd95a109 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-payroll-export.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-payroll-export.service.spec.ts @@ -1,19 +1,21 @@ +import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TimePlanningPnPayrollExportService } from './time-planning-pn-payroll-export.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPayrollExportService', () => { let service: TimePlanningPnPayrollExportService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; let httpTestingController: HttpTestingController; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), } as any; TestBed.configureTestingModule({ @@ -37,28 +39,24 @@ describe('TimePlanningPnPayrollExportService', () => { }); describe('preview', () => { - it('should call correct endpoint with start and end params', (done) => { + it('should call correct endpoint with start and end params', async () => { const mockResponse = { success: true, model: { rows: [], alreadyExportedAt: null } }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.preview('2026-03-20', '2026-04-19').subscribe((result) => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.preview('2026-03-20', '2026-04-19')); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/payroll/preview?start=2026-03-20&end=2026-04-19' ); }); - it('should pass different date ranges correctly', (done) => { + it('should pass different date ranges correctly', async () => { const mockResponse = { success: true, model: null }; mockApiBaseService.get.mockReturnValue(of(mockResponse as any)); - service.preview('2026-01-01', '2026-01-31').subscribe((result) => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.preview('2026-01-01', '2026-01-31')); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.get).toHaveBeenCalledWith( 'api/time-planning-pn/payroll/preview?start=2026-01-01&end=2026-01-31' diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-plannings.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-plannings.service.spec.ts index d9463b73e..302bead89 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-plannings.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-plannings.service.spec.ts @@ -1,16 +1,18 @@ +import {describe, it, expect, beforeEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TimePlanningPnPlanningsService } from './time-planning-pn-plannings.service'; import { ApiBaseService } from 'src/app/common/services'; -import { of } from 'rxjs'; +import { firstValueFrom, of } from 'rxjs'; describe('TimePlanningPnPlanningsService', () => { let service: TimePlanningPnPlanningsService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; beforeEach(() => { mockApiBaseService = { - post: jest.fn(), - put: jest.fn(), - get: jest.fn(), + post: vi.fn(), + put: vi.fn(), + get: vi.fn(), } as any; service = new TimePlanningPnPlanningsService(mockApiBaseService); @@ -21,7 +23,7 @@ describe('TimePlanningPnPlanningsService', () => { }); describe('getPlannings', () => { - it('should call apiBaseService.post with correct parameters', (done) => { + it('should call apiBaseService.post with correct parameters', async () => { const mockRequest = { dateFrom: '2024-01-01', dateTo: '2024-01-07', @@ -33,10 +35,8 @@ describe('TimePlanningPnPlanningsService', () => { const mockResponse = { success: true, model: [] }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.getPlannings(mockRequest).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.getPlannings(mockRequest)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.post).toHaveBeenCalledWith( 'api/time-planning-pn/plannings/index', @@ -44,7 +44,7 @@ describe('TimePlanningPnPlanningsService', () => { ); }); - it('should handle empty response', (done) => { + it('should handle empty response', async () => { const mockRequest = { dateFrom: '2024-01-01', dateTo: '2024-01-07', @@ -56,15 +56,13 @@ describe('TimePlanningPnPlanningsService', () => { const mockResponse = { success: true, model: [] }; mockApiBaseService.post.mockReturnValue(of(mockResponse as any)); - service.getPlannings(mockRequest).subscribe(result => { - expect(result.model).toEqual([]); - done(); - }); + const result = await firstValueFrom(service.getPlannings(mockRequest)); + expect(result.model).toEqual([]); }); }); describe('updatePlanning', () => { - it('should call apiBaseService.put with correct parameters', (done) => { + it('should call apiBaseService.put with correct parameters', async () => { const mockPlanningModel = { id: 123, planHours: 8, @@ -74,10 +72,8 @@ describe('TimePlanningPnPlanningsService', () => { const mockResponse = { success: true }; mockApiBaseService.put.mockReturnValue(of(mockResponse as any)); - service.updatePlanning(mockPlanningModel, 123).subscribe(result => { - expect(result).toEqual(mockResponse as any); - done(); - }); + const result = await firstValueFrom(service.updatePlanning(mockPlanningModel, 123)); + expect(result).toEqual(mockResponse as any); expect(mockApiBaseService.put).toHaveBeenCalledWith( 'api/time-planning-pn/plannings/123', diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-working-hours.service.spec.ts b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-working-hours.service.spec.ts index fa8ba9a60..b990a5db7 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-working-hours.service.spec.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/services/time-planning-pn-working-hours.service.spec.ts @@ -1,3 +1,5 @@ +import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest'; +import type {Mocked} from 'vitest'; import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TimePlanningPnWorkingHoursService } from './time-planning-pn-working-hours.service'; @@ -6,15 +8,15 @@ import { of } from 'rxjs'; describe('TimePlanningPnWorkingHoursService', () => { let service: TimePlanningPnWorkingHoursService; - let mockApiBaseService: jest.Mocked; + let mockApiBaseService: Mocked; let httpTestingController: HttpTestingController; beforeEach(() => { mockApiBaseService = { - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - getBlobData: jest.fn().mockReturnValue(of(new Blob())), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + getBlobData: vi.fn().mockReturnValue(of(new Blob())), } as any; TestBed.configureTestingModule({ diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/testing/detect-changes.ts b/eform-client/src/app/plugins/modules/time-planning-pn/testing/detect-changes.ts new file mode 100644 index 000000000..1e552d099 --- /dev/null +++ b/eform-client/src/app/plugins/modules/time-planning-pn/testing/detect-changes.ts @@ -0,0 +1,14 @@ +import { ChangeDetectorRef } from '@angular/core'; +import { ComponentFixture } from '@angular/core/testing'; + +/** + * `fixture.detectChanges()` for specs that change component or service state + * directly between checks. `ng test` runs zoneless, so a check only refreshes + * views marked dirty, while its no-changes pass looks at every view and throws + * NG0100 on the state change it skipped. Marking the component first gives the + * refresh the app gets from zone.js after an event. + */ +export function detectChanges(fixture: ComponentFixture): void { + fixture.componentRef.injector.get(ChangeDetectorRef).markForCheck(); + fixture.detectChanges(); +} diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/testing/mutation-observer.ts b/eform-client/src/app/plugins/modules/time-planning-pn/testing/mutation-observer.ts new file mode 100644 index 000000000..5b4252953 --- /dev/null +++ b/eform-client/src/app/plugins/modules/time-planning-pn/testing/mutation-observer.ts @@ -0,0 +1,24 @@ +import { afterAll, beforeAll } from 'vitest'; + +/** + * CDK overlays watch their pane with a MutationObserver when they detach. The + * `ng test` DOM hands back an observer without `observe`, so closing a + * connected overlay throws. Call inside a `describe` whose specs open and close + * overlays; it installs a no-op observer only when the environment's is unusable. + */ +export function useWorkingMutationObserver(): void { + const original = globalThis.MutationObserver; + beforeAll(() => { + if (typeof original?.prototype?.observe === 'function') { + return; + } + globalThis.MutationObserver = class { + observe(): void { /* no-op */ } + disconnect(): void { /* no-op */ } + takeRecords(): MutationRecord[] { return []; } + } as unknown as typeof MutationObserver; + }); + afterAll(() => { + globalThis.MutationObserver = original; + }); +} diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/testing/page-providers.ts b/eform-client/src/app/plugins/modules/time-planning-pn/testing/page-providers.ts new file mode 100644 index 000000000..cfd1ca6a3 --- /dev/null +++ b/eform-client/src/app/plugins/modules/time-planning-pn/testing/page-providers.ts @@ -0,0 +1,27 @@ +import { DatePipe } from '@angular/common'; +import { EnvironmentProviders, Provider } from '@angular/core'; +import { provideNativeDateAdapter } from '@angular/material/core'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; +import { provideRouter } from '@angular/router'; +import { AppMenuStateService, AuthStateService } from 'src/app/common/store'; +import { SitesService, TitleService } from 'src/app/common/services'; +import { vi } from 'vitest'; + +/** + * What a page component's frontend children need in a test bed: the page + * subheader (eform-new-subheader) injects the menu/title/auth/sites services and + * the router, the toolbar's datepickers need a DateAdapter, and the grid injects + * the DatePipe the app provides at its root. + */ +export function pageProviders(): Array { + return [ + provideRouter([]), + provideNoopAnimations(), + provideNativeDateAdapter(), + DatePipe, + { provide: AppMenuStateService, useValue: {} }, + { provide: TitleService, useValue: { setTitle: vi.fn() } }, + { provide: AuthStateService, useValue: {} }, + { provide: SitesService, useValue: {} }, + ]; +} diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/testing/register-test-locales.ts b/eform-client/src/app/plugins/modules/time-planning-pn/testing/register-test-locales.ts index 416f83d16..34a28889d 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/testing/register-test-locales.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/testing/register-test-locales.ts @@ -30,8 +30,8 @@ import localeUk from '@angular/common/locales/uk'; * The locale data the running app registers, for a test bed. * * In production `src/main.ts` registers exactly this set before bootstrap, so - * every DatePipe call in the plugin can format in the user's language. A Jest - * bed never runs main.ts, and the host's `src/setup-jest.ts` (which is the only + * every DatePipe call in the plugin can format in the user's language. A test + * bed never runs main.ts, and the host's `src/test-setup.ts` (which is the only * truly global hook, and lives in eform-angular-frontend, not here) does not * register any of it — so a pipe asked for a non-English locale throws * NG0701 "Missing locale data". Angular resolves plain `en` from its built-in diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/testing/source-paths.ts b/eform-client/src/app/plugins/modules/time-planning-pn/testing/source-paths.ts new file mode 100644 index 000000000..9c843ac6f --- /dev/null +++ b/eform-client/src/app/plugins/modules/time-planning-pn/testing/source-paths.ts @@ -0,0 +1,10 @@ +import { join } from 'path'; + +/** + * Specs that read source files from disk. Angular's unit-test builder bundles + * each spec, so `__dirname` no longer points next to the spec. `ng test` runs + * from the host's eform-client directory (locally and in CI), so paths are + * resolved from the working directory instead. + */ +export const CLIENT_ROOT = process.cwd(); +export const MODULE_ROOT = join(CLIENT_ROOT, 'src', 'app', 'plugins', 'modules', 'time-planning-pn'); diff --git a/eform-client/src/app/plugins/modules/time-planning-pn/time-planning-pn.module.ts b/eform-client/src/app/plugins/modules/time-planning-pn/time-planning-pn.module.ts index a34ce89cb..771b6a5fa 100644 --- a/eform-client/src/app/plugins/modules/time-planning-pn/time-planning-pn.module.ts +++ b/eform-client/src/app/plugins/modules/time-planning-pn/time-planning-pn.module.ts @@ -1,10 +1,6 @@ -import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {RouterModule} from '@angular/router'; -import {NgSelectModule} from '@ng-select/ng-select'; -import {TranslateModule} from '@ngx-translate/core'; -import {EformSharedModule} from 'src/app/common/modules/eform-shared/eform-shared.module'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MAT_FORM_FIELD_DEFAULT_OPTIONS} from '@angular/material/form-field'; import {TimePlanningPnRouting} from './time-planning-pn.routing'; import {TimePlanningPnLayoutComponent} from './layouts'; import { @@ -20,37 +16,6 @@ import { WorkdayEntityDialogComponent, DownloadExcelDialogComponent, VersionHistoryModalComponent, PayrollExportDialogComponent } from './components'; -import {MatCard, MatCardContent, MatCardHeader} from '@angular/material/card'; -import { - MAT_FORM_FIELD_DEFAULT_OPTIONS, MatError, - MatFormField, - MatLabel, - MatPrefix, - MatSuffix -} from '@angular/material/form-field'; -import {MatInput} from '@angular/material/input'; -import {MatButton, MatIconButton} from '@angular/material/button'; -import {MatSlideToggle} from '@angular/material/slide-toggle'; -import { - MatDatepicker, - MatDatepickerInput, - MatDatepickerToggle, - MatDateRangeInput, - MatDateRangePicker, MatEndDate, MatStartDate -} from '@angular/material/datepicker'; -import {MtxGrid} from '@ng-matero/extensions/grid'; -import {NgxChartsModule} from '@swimlane/ngx-charts'; -import {MatIcon} from '@angular/material/icon'; -import {MatTooltip} from '@angular/material/tooltip'; -import {NgxMaskDirective} from 'ngx-mask'; -import {NgxMaterialTimepickerModule} from 'ngx-material-timepicker'; -import {MatTab, MatTabGroup} from '@angular/material/tabs'; -import {MatCheckbox} from '@angular/material/checkbox'; -import {MatRadioButton, MatRadioGroup} from '@angular/material/radio'; -import {MatDialogActions, MatDialogClose, MatDialogContent, MatDialogTitle} from '@angular/material/dialog'; -import {MtxSelect} from '@ng-matero/extensions/select'; -import {OverlayModule} from '@angular/cdk/overlay'; -import {CdkScrollable} from '@angular/cdk/scrolling'; import {HelpIconComponent} from './help/components/help-icon/help-icon.component'; import {HelpHintComponent} from './help/components/help-hint/help-hint.component'; import {HelpPanelComponent} from './help/components/help-panel/help-panel.component'; @@ -58,56 +23,8 @@ import {HelpTourComponent} from './help/components/help-tour/help-tour.component @NgModule({ imports: [ - CommonModule, - TranslateModule, - FormsModule, - NgSelectModule, - EformSharedModule, - RouterModule, + MatDialogModule, TimePlanningPnRouting, - ReactiveFormsModule, - MatCardHeader, - MatTab, - MatDialogActions, - MatDialogTitle, - MatDialogContent, - MatDialogClose, - MtxSelect, - MatCard, - MatCheckbox, - MatRadioGroup, - MatRadioButton, - MatCardContent, - MatIconButton, - MatFormField, - MatInput, - MatButton, - MatTabGroup, - MatSlideToggle, - MatLabel, - MatDatepicker, - MatDatepickerInput, - MatDatepickerToggle, - MatSuffix, - MtxGrid, - NgxChartsModule, - MatIcon, - MatTooltip, - NgxMaskDirective, - NgxMaterialTimepickerModule, - MatDateRangeInput, - MatDateRangePicker, - MatStartDate, - MatEndDate, - MatPrefix, - MatError, - OverlayModule, - // The day-cell dialog declares its own overflow container inside - // mat-dialog-content; ScrollDispatcher only sees containers marked - // cdkScrollable, and the help popovers dismiss on scroll. - CdkScrollable - ], - declarations: [ TimePlanningPnLayoutComponent, TimePlanningSettingsComponent, WorkdayEntityDialogComponent,