Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CPS-562: E2E test Basic Structure #1

Merged
merged 28 commits into from
Jun 3, 2021
Merged

Conversation

deb1990
Copy link
Contributor

@deb1990 deb1990 commented Apr 30, 2021

Overview

This PR Creates a basic structure for E2E tests using Playwright, Jest and Gulp.

Technical Details

Refer https://github.com/compucorp/cases-product-suite-e2e-tests/blob/CPS-562-e2e-basic-setup/readme.md.

@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from 63ff0b9 to 52738f8 Compare May 3, 2021 03:49
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from a742049 to 02da8ca Compare May 3, 2021 07:01
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from f71fc41 to 711b2e4 Compare May 3, 2021 07:05
@deb1990 deb1990 changed the title CPS-562: Basic Structure CPS-562: E2E test Basic Structure May 3, 2021
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from 897fea6 to 7950a8e Compare May 5, 2021 12:51
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch 5 times, most recently from 708a722 to 0d05713 Compare May 5, 2021 18:26
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from 0d05713 to cda7776 Compare May 5, 2021 18:29
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch 2 times, most recently from 9d00133 to 30ab776 Compare May 6, 2021 19:35
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch 6 times, most recently from c4554f4 to 3bab997 Compare May 7, 2021 06:37
@deb1990 deb1990 force-pushed the CPS-562-e2e-basic-setup branch from 3bab997 to dd3f74a Compare May 7, 2021 06:47
* @returns {Promise<any>} promise
*/
async waitForPageLoad (page: Page): Promise<any> {
return await page.waitForSelector('.civicase__case-filter-panel__button');
Copy link

@pubududp pubududp May 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deb1990 Please move all the selectors within page classes to variables and then reuse the variables inside the tests. To make it a bit more accessible we can create array and pop all the elements inside the array so we can refer to elements by "PageName.selectors.ElementName"

eg:
static selectors = {
usernameTextBox: 'input[data-test="username"]',
passwordTextBox: 'input[data-test="password"]',
loginButton: 'input[data-test="login-button"]'
};

This would be useful when tests are using elements from more than one page in E2E scenarios.

Copy link
Member

@reneolivo reneolivo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deb1990 @tunbola @lisandro-compucorp have we considered the use of Cucumber for writing the spec files?

I have the Gherkin syntax much more readable and explanatory. I think writing new tests can be easier as well.

Instead of having an intimidating file such as
tests/prospects/manage-prospects/prospect-page-title.spec.ts

We can have something like

Feature: Prospect Dashboard

Scenario: Prospect Dashboard Title
  When I log in as a "Prospect User"
  And I navigate to the "Prospect Dashboard"
  Then the page title should be "Manage Prospectings"

Scenario: Restricted Access
  When I log in as a "CiviCRM User"
  And I navigate to the "Prospect Dashboard"
  Then I should get an access denied message

The cool thing about these examples is that we can reuse sentences in other spec files.
The "Given I log in as a *" can be defined in code as follows:

Given("I log in as a {string}", function (userRole) {
  await browser.loginUsingCookiesAs(userRole);
});

Once we define things like "login as" or "navigate to", we don't need to define these in the code again, and again. We just need to mention the steps in the spec files.

The Gherkin files also remove a lot of code that is necessary to run tests but not to understand what
we want to test such as

  beforeAll(async () => {
    await browser.setup();
  });

  afterAll(async () => {
    await browser.close();
  });

  beforeEach(async () => {
    page = await browser.newPage();

    manageProspects = new ManageProspects(browser);
  });

  afterEach(async () => {
    await browser.takeScreenshotWhenFailedAndClose(page);
  });

And even code that is perfectly readable for programmers can be more easily understood in a test context:

await manageProspects.navigate(page);
await manageProspects.waitForPageLoad(page);

vs

And I navigate to "Prospect Dashboard"

Let me know what you guys think and we can discuss in detail.

export default jestTask;

/**
* @returns {Promise<void>} promise
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why document the type when TypeScript is already doing that?

I don't see much value in adding these unless there's a specific note to add.

/**
* @returns {Promise<void>} promise
*/
async function jestTask (): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think the return value is inferred by TypeScript because there is no return and because of async. TypeScript would usually infer the return value and there is no need to define it in most cases. Unless we want to make it a rule to always define it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah Standard JS is enforcing this, which is fine because now we are documenting the return type anymore using JSDoc.

option_group_id?: string
name?: string
definition?: any
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can define every possible parameter here, but different API endpoints have different parameters. This list might grow exponentially over time.

The actual type for API parameters would be

{
  [key: string]: any,
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, in that case, should we not use any type? Because [key: string]: any, defeats the purpose, or what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[key: string]: any is a bit more specific than any because I don't think you can pass numbers to that parameter.

Comment on lines 19 to 22
*/
constructor (public browser: BrowserService) {
super(browser);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to define the constructor method if we are not going to have a different behaviour from the base class.

/**
* @returns {string} page title
*/
abstract getPageTitle (): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to redefine this one either.

/**
* @returns {string} page title
*/
getPageUrl (): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to define the return type. TypeScript knows that you are returning a string.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is enforced by StandardJS.

Comment on lines 11 to 13
constructor (public browser: BrowserService) {
super(browser);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove

Comment on lines 13 to 15
constructor (public browser: BrowserService) {
super(browser);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove

* @param {CiviApiQuery} params parameters
* @returns {object[]} created entities
*/
create (params: CiviApiParam[]): object[] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From experience, I try not to add the return type at all. TypeScript is smart enough to determine the return type. And if it doesn't figure it out, it means one of your function call return types is not properly defined. It's easier to debug proper TypeScript when you don't add the return type hint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is enforced by StandardJS.

Comment on lines +2 to +3
import BrowserService from '../../../src/services/utils/browser.service';
import { ManageApplications } from '../../../src/pages/awards/manage-applications.page';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can import things from the root if configured:

https://stackoverflow.com/questions/42123382/import-module-from-root-path-in-typescript

import BrowserService from '../../../src/services/utils/browser.service';
// vs
import BrowserService from 'services/utils/browser.service';

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better when you don't have to count the number of folders to go back.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolute paths are currently not supported in ts-node. wclr/ts-node-dev#95

I tried some of the solutions mentioned, but its not working and I have already spent a lot of time in this. Lets leave this for now, I will keep trying.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about this, but if you want to check this in the future it seems this can work:
https://dev.to/larswaechter/path-aliases-with-typescript-in-nodejs-4353

I have also created a package.json file in the src folder and then reference it in the root package.json. Like this:

// /package.json
{
  "dependencies": {
    "cases-e2e": "./src"
  }
}
// /src/pacakge.json
{
  "name": "cases-e2e"
}

Then on any TS file I can do this:

import something from 'cases-e2e/a/path/from/src/something';


- name: Install E2E Test Suite
run: |
git clone https://github.com/compucorp/cases-product-suite-e2e-tests.git --branch CPS-562-e2e-basic-setup
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should switch the branch to be cloned here to master

@deb1990 deb1990 merged commit c7e1197 into master Jun 3, 2021
@deb1990 deb1990 deleted the CPS-562-e2e-basic-setup branch June 3, 2021 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants