Skip to content

Commit

Permalink
fix(workbench/host): retain focus on element that closed popup due to…
Browse files Browse the repository at this point in the history
… loss of focus

Previously, when a microfrontend popup was closed due to loss of focus, that is, when an element outside the popup was clicked, the focus did not remain on that element that caused the popup to lose focus.
  • Loading branch information
danielwiehl authored and Marcarrian committed Dec 7, 2022
1 parent 9293464 commit 29c82bf
Show file tree
Hide file tree
Showing 21 changed files with 359 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const routes: Routes = [
},
{
path: 'test-pages',
loadChildren: (): any => import('./test-pages/test-pages-routing.module').then(m => m.TestPagesRoutingModule),
loadChildren: (): any => import('./test-pages/routes').then(m => m.routes),
},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<sci-form-field label="Input Field">
<input class="e2e-input">
</sci-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: block;
padding: 1em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2022 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

import {Component} from '@angular/core';
import {SciFormFieldModule} from '@scion/components.internal/form-field';

@Component({
selector: 'app-input-field-test-page',
templateUrl: './input-field-test-page.component.html',
styleUrls: ['./input-field-test-page.component.scss'],
standalone: true,
imports: [
SciFormFieldModule,
],
})
export class InputFieldTestPageComponent {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
* SPDX-License-Identifier: EPL-2.0
*/

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {Routes} from '@angular/router';

const routes: Routes = [
export const routes: Routes = [
{
path: 'bulk-navigation-test-page',
loadComponent: (): any => import('./bulk-navigation-test-page/bulk-navigation-test-page.component').then(m => m.BulkNavigationTestPageComponent),
Expand All @@ -20,11 +19,8 @@ const routes: Routes = [
path: 'view-properties-test-page',
loadComponent: (): any => import('./view-properties-test-page/view-properties-test-page.component').then(m => m.ViewPropertiesTestPageComponent),
},
{
path: 'input-field-test-page',
loadComponent: (): any => import('./input-field-test-page/input-field-test-page.component').then(m => m.InputFieldTestPageComponent),
},
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class TestPagesRoutingModule {
}
13 changes: 1 addition & 12 deletions apps/workbench-testing-app/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,7 @@ const routes: Routes = [
},
{
path: 'test-pages',
children: [
{
path: 'bulk-navigation-test-page',
loadComponent: (): any => import('./test-pages/bulk-navigation-test-page/bulk-navigation-test-page.component').then(m => m.BulkNavigationTestPageComponent),
data: {[WorkbenchRouteData.title]: 'Bulk Navigation Test', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-bulk-navigation'},
},
{
path: 'view-route-data-test-page',
loadChildren: (): any => import('./test-pages/view-route-data-test-page/view-route-data-test-page.module').then(m => m.ViewRouteDataTestPageModule),
data: {[WorkbenchRouteData.title]: 'View Route Data Test', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-view-route-data'},
},
],
loadChildren: (): any => import('./test-pages/routes').then(m => m.routes),
},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<sci-form-field label="Input Field">
<input class="e2e-input">
</sci-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:host {
display: block;
padding: 1em;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2022 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

import {Component} from '@angular/core';
import {SciFormFieldModule} from '@scion/components.internal/form-field';

@Component({
selector: 'app-input-field-test-page',
templateUrl: './input-field-test-page.component.html',
styleUrls: ['./input-field-test-page.component.scss'],
standalone: true,
imports: [
SciFormFieldModule,
],
})
export class InputFieldTestPageComponent {
}
30 changes: 30 additions & 0 deletions apps/workbench-testing-app/src/app/test-pages/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2022 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

import {Routes} from '@angular/router';
import {WorkbenchRouteData} from '@scion/workbench';

export const routes: Routes = [
{
path: 'bulk-navigation-test-page',
loadComponent: (): any => import('./bulk-navigation-test-page/bulk-navigation-test-page.component').then(m => m.BulkNavigationTestPageComponent),
data: {[WorkbenchRouteData.title]: 'Bulk Navigation Test', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-bulk-navigation'},
},
{
path: 'view-route-data-test-page',
loadChildren: (): any => import('./view-route-data-test-page/view-route-data-test-page.module').then(m => m.ViewRouteDataTestPageModule),
data: {[WorkbenchRouteData.title]: 'View Route Data Test', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-view-route-data'},
},
{
path: 'input-field-test-page',
loadComponent: (): any => import('./input-field-test-page/input-field-test-page.component').then(m => m.InputFieldTestPageComponent),
data: {[WorkbenchRouteData.title]: 'Input Field Test Page', [WorkbenchRouteData.heading]: 'Workbench E2E Testpage', [WorkbenchRouteData.cssClass]: 'e2e-test-input-field'},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test.describe('Bulk Navigation', () => {
test('should navigate to multiple views if waiting for each navigation to complete', async ({appPO, microfrontendNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

const bulkNavigationTestPagePO = await BulkNavigationTestPagePO.navigateTo(appPO, microfrontendNavigator);
const bulkNavigationTestPagePO = await BulkNavigationTestPagePO.openInNewTab(appPO, microfrontendNavigator);
await bulkNavigationTestPagePO.enterViewCount(10);
await bulkNavigationTestPagePO.enterCssClass('bulk-navigation-test-target');
await bulkNavigationTestPagePO.clickNavigateAwait();
Expand All @@ -28,7 +28,7 @@ test.describe('Bulk Navigation', () => {
test('should navigate to multiple views if not waiting for each navigation to complete', async ({appPO, microfrontendNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

const bulkNavigationTestPagePO = await BulkNavigationTestPagePO.navigateTo(appPO, microfrontendNavigator);
const bulkNavigationTestPagePO = await BulkNavigationTestPagePO.openInNewTab(appPO, microfrontendNavigator);
await bulkNavigationTestPagePO.enterViewCount(10);
await bulkNavigationTestPagePO.enterCssClass('bulk-navigation-test-target');
await bulkNavigationTestPagePO.clickNavigateNoAwait();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class BulkNavigationTestPagePO {
await waitUntilStable(() => this._appPO.page.url(), {probeInterval: 500});
}

public static async navigateTo(appPO: AppPO, microfrontendNavigator: MicrofrontendNavigator): Promise<BulkNavigationTestPagePO> {
public static async openInNewTab(appPO: AppPO, microfrontendNavigator: MicrofrontendNavigator): Promise<BulkNavigationTestPagePO> {
// Register the test page as view.
const registerCapabilityPagePO = await microfrontendNavigator.openInNewTab(RegisterWorkbenchCapabilityPagePO, 'app1');
await registerCapabilityPagePO.registerCapability({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2022 Swiss Federal Railways
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

import {AppPO} from '../../../app.po';
import {Locator} from '@playwright/test';
import {isActiveElement} from '../../../helper/testing.util';
import {ElementSelectors} from '../../../helper/element-selectors';
import {MicrofrontendNavigator} from '../../microfrontend-navigator';
import {RegisterWorkbenchCapabilityPagePO} from '../register-workbench-capability-page.po';
import {ViewPO} from '../../../view.po';

export class InputFieldTestPagePO {

private readonly _locator: Locator;
public readonly view: ViewPO;

constructor(appPO: AppPO, viewId: string) {
this.view = appPO.view({viewId});
this._locator = appPO.page.frameLocator(ElementSelectors.routerOutletFrame(viewId)).locator('app-input-field-test-page');
}

public async clickInputField(): Promise<void> {
await this._locator.locator('input.e2e-input').click();
}

public async isActiveElement(): Promise<boolean> {
return isActiveElement(this._locator.locator('input.e2e-input'));
}

public static async openInNewTab(appPO: AppPO, microfrontendNavigator: MicrofrontendNavigator): Promise<InputFieldTestPagePO> {
// Register the test page as view.
const registerCapabilityPagePO = await microfrontendNavigator.openInNewTab(RegisterWorkbenchCapabilityPagePO, 'app1');
await registerCapabilityPagePO.registerCapability({
type: 'view',
qualifier: {test: 'input-field'},
properties: {
path: 'test-pages/input-field-test-page',
cssClass: 'e2e-test-input-field',
title: 'Input Field Test Page',
pinToStartPage: true,
},
});
await registerCapabilityPagePO.viewTabPO.close();

// Navigate to the view.
const startPagePO = await appPO.openNewViewTab();
await startPagePO.clickTestCapability('e2e-test-input-field', 'app1');

// Create the page object.
const view = await appPO.view({cssClass: 'e2e-test-input-field'});
await view.waitUntilPresent();
return new InputFieldTestPagePO(appPO, await view.getViewId());
}
}
102 changes: 93 additions & 9 deletions projects/scion/e2e-testing/src/workbench-client/popup.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {test} from '../fixtures';
import {PopupPagePO} from './page-object/popup-page.po';
import {RegisterWorkbenchCapabilityPagePO} from './page-object/register-workbench-capability-page.po';
import {PopupOpenerPagePO} from './page-object/popup-opener-page.po';
import {InputFieldTestPagePO as MicrofrontendInputFieldTestPagePO} from './page-object/test-pages/input-field-test-page.po';
import {InputFieldTestPagePO as WorkbenchInputFieldTestPagePO} from '../workbench/page-object/test-pages/input-field-test-page.po';

test.describe('Workbench Popup', () => {

Expand Down Expand Up @@ -292,6 +294,37 @@ test.describe('Workbench Popup', () => {
await expect(await popupPO.getBoundingBox()).toEqual(popupClientRectInitial);
});

test('should provide the popup\'s capability', async ({appPO, microfrontendNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

// register testee popup
const registerCapabilityPagePO = await microfrontendNavigator.openInNewTab(RegisterWorkbenchCapabilityPagePO, 'app1');
await registerCapabilityPagePO.registerCapability({
type: 'popup',
qualifier: {component: 'testee'},
properties: {
path: 'popup',
cssClass: 'testee',
},
});

// open the popup
const popupOpenerPagePO = await microfrontendNavigator.openInNewTab(PopupOpenerPagePO, 'app1');
await popupOpenerPagePO.enterQualifier({component: 'testee'});
await popupOpenerPagePO.clickOpen();

// expect the popup of this app to display
const popupPagePO = new PopupPagePO(appPO, 'testee');
await expect(await popupPagePO.getPopupCapability()).toEqual(expect.objectContaining({
qualifier: {component: 'testee'},
type: 'popup',
properties: expect.objectContaining({
path: 'popup',
cssClass: ['testee'],
}),
}));
});

test.describe('view context', () => {

test('should hide the popup when its contextual view (if any) is deactivated, and then display the popup again when activating it', async ({appPO, microfrontendNavigator}) => {
Expand Down Expand Up @@ -560,7 +593,7 @@ test.describe('Workbench Popup', () => {
await expect(await popupPagePO.popupPO.isVisible()).toBe(true);
});

test('should provide the popup\'s capability', async ({appPO, microfrontendNavigator}) => {
test('should remain focus on the element that caused the popup to lose focus when focusing element on a microfrontend view', async ({appPO, microfrontendNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

// register testee popup
Expand All @@ -574,21 +607,72 @@ test.describe('Workbench Popup', () => {
},
});

// open the popup
// Open popup opener page
const popupOpenerPagePO = await microfrontendNavigator.openInNewTab(PopupOpenerPagePO, 'app1');
// Open test view
const inputFieldPagePO = await MicrofrontendInputFieldTestPagePO.openInNewTab(appPO, microfrontendNavigator);
// Move test page to the right
await inputFieldPagePO.view.viewTab.dragToPart({region: 'east'});

// Open popup
await popupOpenerPagePO.enterQualifier({component: 'testee'});
await popupOpenerPagePO.enterCloseStrategy({closeOnFocusLost: true});
await popupOpenerPagePO.clickOpen();

// expect the popup of this app to display
// Expect popup to have focus.
const popupPagePO = new PopupPagePO(appPO, 'testee');
await expect(await popupPagePO.getPopupCapability()).toEqual(expect.objectContaining({
qualifier: {component: 'testee'},
await popupPagePO.waitForFocus();

// Click the input field to make popup lose focus
await inputFieldPagePO.clickInputField();

// Expect popup to be closed
await popupPagePO.popupPO.waitUntilClosed();
await expect(await popupPagePO.popupPO.isVisible()).toBe(false);

// Expect focus to remain in the input field that caused focus loss of the popup.
await expect(await inputFieldPagePO.isActiveElement()).toBe(true);
});

test('should remain focus on the element that caused the popup to lose focus when focusing element on a non-microfrontend view', async ({appPO, microfrontendNavigator, workbenchNavigator}) => {
await appPO.navigateTo({microfrontendSupport: true});

// register testee popup
const registerCapabilityPagePO = await microfrontendNavigator.openInNewTab(RegisterWorkbenchCapabilityPagePO, 'app1');
await registerCapabilityPagePO.registerCapability({
type: 'popup',
properties: expect.objectContaining({
qualifier: {component: 'testee'},
properties: {
path: 'popup',
cssClass: ['testee'],
}),
}));
cssClass: 'testee',
},
});

// Open popup opener page
const popupOpenerPagePO = await microfrontendNavigator.openInNewTab(PopupOpenerPagePO, 'app1');
// Open test view
const inputFieldPagePO = await WorkbenchInputFieldTestPagePO.openInNewTab(appPO, workbenchNavigator);
// Move test page to the right
await inputFieldPagePO.view.viewTab.dragToPart({region: 'east'});

// Open popup
await popupOpenerPagePO.enterQualifier({component: 'testee'});
await popupOpenerPagePO.enterCloseStrategy({closeOnFocusLost: true});
await popupOpenerPagePO.clickOpen();

// Expect popup to have focus.
const popupPagePO = new PopupPagePO(appPO, 'testee');
await popupPagePO.waitForFocus();

// Click the input field to make popup lose focus
await inputFieldPagePO.clickInputField();

// Expect popup to be closed
await popupPagePO.popupPO.waitUntilClosed();
await expect(await popupPagePO.popupPO.isVisible()).toBe(false);

// Expect focus to remain in the input field that caused focus loss of the popup.
await expect(await inputFieldPagePO.isActiveElement()).toBe(true);
});
});
});
Loading

0 comments on commit 29c82bf

Please sign in to comment.