Page objects initialization for Playwright - automation

In my current Playwright test automation project, we use page object model pattern.
At the beginning I was initializing all page objects within a test (usually in before hooks)
test('something', async ({ page}) => {
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);
//...
const exportModal = new ExportModal(page);
//then user these objects
});
Then as we grow these started looking a bit messy and I decided to somehow aggregate these objects initialization
export class MyApp {
readonly page: Page;
readonly loginPage: LoginPage;
readonly dashboardPage: DashboardPage;
constructor (page: Page){
this.page = page;
this.loginPage = new LoginPage(page);
this.dashboardPage = new DashboardPage(page);
//...
this.exportModal = new ExportModal(page);
}
}
And inside tests
test('something', async ({ page}) => {
const myApp = new MyApp(page);
myApp.loginpage.doSomething();
myApp.dashboardPage.doSomethingElse();
});
Am I doing it right or there is some better approach to initialize page objects?

Related

Decorating Lifecycle Hooks in vue.js

Does anyone know a good way to decorate lifecycle hooks from within the vue component? I am adding a function in the mounted in my component that gets called from the app.vue when the screen size changes. When I navigate away from the page I want to remove this function. Currently I have to do it in the destroy method on the component. If I was able to pass in the destroyed method on the component when I am creating the listener and decorate it with the logic to destroy the listener I would not have to worry about someone adding the listener on a component and not adding the removal of the listener in the destroy method.
public AddUpdateIsMobileFunction(fn: (screenType: any) => void, destroyHook: any): any {
this.updateIsMobileFunctions.push(fn);
const currentFunctionPlacementInArray: number = this.updateIsMobileFunctions.length;
const destroySelf: () => void = () => {
this.updateIsMobileFunctions.splice(currentFunctionPlacementInArray, 1);
}
const originalDestroyHook: any = _.deepClone(destroyHook)
destroyHook = () => {
originalDestroyHook();
destroySelf();
}
return destroySelf;
}
In the component
private mounted(): void {
this.destroyIsMobileFunction = this.$Global.AddUpdateIsMobileFunction((screenType: any) => {
this.isMobile = screenType.isMobile;
console.log("test is mobile fn")
}, pass in the destroyed function here )
}
remove the function below
private beforeDestroy(): void {
console.log('in the destroy function in the vue component')
this.destroyIsMobileFunction();
}

How to `setUp` a `WidgetTester` for Multiple Tests in Flutter

1. The Problem
The testWidgets function is apparently only a subcase of the test function.
A use case I'm trying to solve right now is to pump the same widget for multiple testWidgets, a setUp for multiple testWidgets. However, how can I do this if it creates a new instance inside each test?
I've tried to initialize a WidgetTester outside the tests, in the main(), but WidgetTester has only a private constructor:
class WidgetTester
extends WidgetController
implements HitTestDispatcher, TickerProvider {
WidgetTester._(TestWidgetsFlutterBinding binding) : super(binding) {
if (binding is LiveTestWidgetsFlutterBinding)
binding.deviceEventDispatcher = this;
}
I don't quite get how the Flutter team made this work, but initializing a WidgetTester in the same way they did inside the testWidgets function isn't working for me:
final TestWidgetsFlutterBinding binding
= TestWidgetsFlutterBinding.ensureInitialized()
as TestWidgetsFlutterBinding;
final WidgetTester tester = WidgetTester._(binding);
2. An Example
A simple example would be to try to break down the tests of the Flutter demo that is created with each new Flutter project from flutter create. In it, we could try to separate the initial setup test of the app from the tapping action test:
testWidgets('Initial setup', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
});
testWidgets('Increment the counter on tap', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
The idea would be to try to move the await tester.pumpWidget(MyApp()); into a setUp function.
Below is what looks like the current way to solve for this in Flutter.
To summarize:
Create the group(..) structure inside main()
Create your own private methods from inside that structure, for each group of testing you want. For each of these private methods:
Pass in the WidgetTester instance
Let them be async
And then you should only have a single call to testWidgets(..)
Inside this method, is where you call the private methods you set up to distribute test logic
Call each of these with await, so they don't run concurrently
So far I didn't find a way for the output to indicate each "sub-test" it ran, so just using print(...) statements for now.
This is a demo for some QR Code logic:
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:qr_code_demo/app/appRoutes.dart';
import 'package:qr_code_demo/view/appHome.dart';
import 'package:qr_code_demo/view/qrScanner.dart';
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
void main() {
group('MainPage navigation tests', () {
NavigatorObserver mockObserver;
_loadAppHomeScreen(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
routes: AppRoutes.getRouteMap(),
home: AppHomeScreen(),
navigatorObservers: [mockObserver],
),
);
}
setUp(() {
mockObserver = MockNavigatorObserver();
});
Future<Null> _verifyLayoutElements(WidgetTester tester) async {
print('_verifyLayoutElements');
expect(find.byIcon(Icons.scanner), findsOneWidget);
expect(find.byType(FloatingActionButton), findsOneWidget);
expect(find.byType(RaisedButton), findsOneWidget);
}
Future<Null> _navigateToQrScannerScreen(WidgetTester tester) async {
print('_navigateToQrScannerScreen');
await tester.tap(find.byIcon(Icons.scanner));
await tester.pumpAndSettle();
verify(mockObserver.didPush(any, any));
expect(find.byType(AppHomeScreen), findsNothing);
expect(find.byType(QrScannerScreen), findsOneWidget);
}
testWidgets('AppHomeScreen WidgetTester', (WidgetTester tester) async {
await _loadAppHomeScreen(tester);
await _verifyLayoutElements(tester);
await _navigateToQrScannerScreen(tester);
});
});
}
Thanks to:
https://iiro.dev/2018/08/22/writing-widget-tests-for-navigation-events/
Scroll to code for this file: test/navigation_test.dart
====
And double-thanks, because the navigation testing logic including with this example is thanks to #iiro's post: https://stackoverflow.com/a/51983194/2162226
Here is the appRoutes.dart file:
import 'package:qr_code_demo/view/appHome.dart';
import 'package:qr_code_demo/view/qrScanner.dart';
class AppRoutes {
static const String AppHome = 'AppHome';
static const String QrScanner = 'QrScanner';
static String initialRoute() {
return AppHome;
}
static getRouteMap() {
return {
AppRoutes.AppHome: (context) => AppHomeScreen(),
AppRoutes.QrScanner: (context) => QrScannerScreen()
};
}
}
The answer is setUpAll() function. Use it first in the main method, outside of group and it runs only once.

Connexion object singleton in react native

I'm trying to create a singleton service class in which I instanciate a connection object, which connect to the backend, in order to reuse the connection object in every component, so I've done that:
const {
Kuzzle,
WebSocket
} = require('kuzzle-sdk');
class KuzzleService {
static instance = null;
static async createInstance() {
var object = new KuzzleService();
object.kuzzle = new Kuzzle(
new WebSocket('localhost'),{defaultIndex: 'index'}
);
await object.kuzzle.connect();
const credentials = { username: 'user', password: 'pass' };
const jwt = await object.kuzzle.auth.login('local', credentials);
return object;
}
static async getInstance () {
if (!KuzzleService.instance) {
KuzzleService.instance = await KuzzleService.createInstance();
}
return KuzzleService.instance;
}
}
const kuzzleService = KuzzleService.getInstance();
export default kuzzleService;
But when I import the service in a component as follow:
import kuzzleService from "../services/kuzzle-service.js";
And I print that:
async componentDidMount(){
console.log(JSON.stringify(kuzzleService.kuzzle));
}
It gives me "undefined". Should I import the service another way ?
This is probably because when you export kuzzleService, the promise given by .getInstance() isn't resolved yet.
You should export the .getInstance function and await it in componentDidMount, like that:
export default KuzzleService; // export the singleton directly
async componentDidMount(){
const kuzzle = await KuzzleService.getInstance();
console.log(kuzzle);
}

Send a string array from controller to ASP.NET Core server side rendering JavaScript

Here's what I'm trying to achieve - I want to send a string array from my ASP.NET controller to the JavaScript (compiled from TypeScript) that's run both on server side (as prerendering) and on client side (as initial state). I'm using React-Redux template from Visual Studio.
My Controller method:
public IActionResult Index()
{
ViewData["Services"] = new string[] { "ServiceA", "ServiceB" };
return View();
}
My cshtml view file:
#{
ViewData["Title"] = "Home Page";
dynamic data = new
{
services = ViewData["Services"] as string[]
};
}
<div id="react-app" asp-prerender-module="ClientApp/dist/main-server" asp-prerender-data=data>Loading...</div>
#section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}
My boot-server file portion:
return new Promise<RenderResult>((resolve, reject) => {
actionCreators.addServices(params.data.services);
.......
My actionCrators object above is
export const actionCreators = {
addServices: (services: string[]): AppThunkAction<KnownAction> => (dispatch, getState) => {
dispatch({ type: 'ADD_SERVICES', services: services });
}
};
which is mapped to this reducer:
const unloadedState: ServicesState = { services: [] };
export const reducer: Reducer<ServicesState> = (state: ServicesState, incomingAction: Action) => {
const action = incomingAction as KnownAction;
switch (action.type) {
case 'ADD_SERVICES':
return {
services: action.services
};
default:
// The following line guarantees that every action in the KnownAction union has been covered by a case above
const exhaustiveCheck = action;
}
// For unrecognized actions (or in cases where actions have no effect), must return the existing state
// (or default initial state if none was supplied)
return state || unloadedState;
};
If I set some values in the unloadedState array, the services array in initialState gets populated on server side and I can see them in view sources of my page. So my flux loop logic is fine. I'm suspecting the issue is passing string array from controller to asp-prerender-data via ViewData.
Please help.

Shadow DOM and testing it via Jasmine

I have a webcomponent that creates a shadow DOM and adds some html to its shadowRoot.
class SomeThing extends HTMLElement {
attachedCallback () {
this.el = this.createShadowRoot();
this.render();
}
render () {
this.el.innerHTML = '<h1>Hello</h1>';
}
}
export default SomeThing;
And I am compiling it with the help of webpack and its babel-core and babel-preset-es2015 plugins.
Also I am using Karma and Jasmine to write my Unit Test. This is what it looks like.
describe('some-thing', function () {
var someElement;
beforeEach(function () {
someElement = document.createElement('some-thing');
});
it('created element should match string representation', function () {
var expectedEl = '<some-thing></some-thing>';
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.innerHTML).toBe(expectedEl);
});
it('created element should have shadow root', function () {
var wrapper = document.createElement('div');
wrapper.appendChild(someElement);
expect(wrapper.querySelector('some-thing').shadowRoot).not.toBeNull();
})
});
I want to see if there is something in the shadowRoot of my element, and want to write test cases for the HTML and events created inside the shadowRoot. But the second test is failing. It is not able to add shadowRoot to the some-element DOM.
If anyone can help me out, that would be helpful.
I am also uploading the full test working project on Github. You can access it via this link https://github.com/prateekjadhwani/unit-tests-for-shadow-dom-webcomponents
Thanks in advance
I had a similar problem testing a web component but in my case I am using lit-element from polymer/lit-element. Lit-element provides life cycle hooks, template rendering using lit-html library (documentation).
So this is my problem and how I solved. I noticed that the component was added and the class executed constructor and I had access to public methods using:
const element = document.querySelector('my-component-name')
element.METHOD();
element.VARIABLE
But it never reached the hook firstUpdated, so I thought the problem was the speed the test executes vs the speed component is created. So I used the promised provided by lit-element API (updateComplete):
Note: I use mocha/chai instead of Jasmine
class MyComponent extends LitElement {
render() {
return html`<h1>Hello</h1>`
}
}
customElements.define('my-component', TodoApp);
let element;
describe('main', () => {
beforeEach(() => {
element = document.createElement("my-component");
document.querySelector('body').appendChild(element);
});
describe('test', () => {
it('Checks that header tag was added to shadowRoot', (done) => {
(async () => {
const res = await element.updateComplete;
const header = element.shadowRoot.querySelector('h1');
assert.notEqual(header, null);
done();
})();
});
});
});
So, my advice is create a promise and resolve it when the render function is executed, use the promise to sync the creation of the component with tests.
I am using this repository to test concepts
https://github.com/correju/polymer-playground