Skip to content

Commit

Permalink
Add playwright qa scenarios (#480)
Browse files Browse the repository at this point in the history
* initial round of new tests
* add more tests and improve test descriptions
* reenable github action and extend timeout for server start
  • Loading branch information
stephenkilbourn authored Sep 24, 2024
1 parent 2cdf3c4 commit 6f7e6d9
Show file tree
Hide file tree
Showing 36 changed files with 607 additions and 128 deletions.
61 changes: 35 additions & 26 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,29 +82,38 @@ jobs:
- name: Unit Test
run: yarn test

# playwright_test:
# timeout-minutes: 60
# runs-on: ubuntu-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# with:
# submodules: recursive
# - name: Use Node.js ${{ env.NODE }}
# uses: actions/setup-node@v4
# with:
# node-version: ${{ env.NODE }}
# - name: Install dependencies
# run: yarn
# - name: Install UI
# run: ./.veda/setup
# - name: Install Playwright Browsers
# run: npx playwright install --with-deps
# - name: Run Playwright tests
# run: MAPBOX_TOKEN="${{secrets.MAPBOX_TOKEN}}" yarn test:e2e
# - uses: actions/upload-artifact@v4
# if: always()
# with:
# name: playwright-report
# path: playwright-report/
# retention-days: 30
playwright_test:
timeout-minutes: 60
needs: prep
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Use Node.js ${{ env.NODE }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE }}
- name: Cache node_modules
uses: actions/cache@v2
id: cache-node-modules
with:
path: |
node_modules
.veda/ui/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package.json') }}
- name: Install dependencies
run: yarn
- name: Install UI
run: ./.veda/setup
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: MAPBOX_TOKEN="${{secrets.MAPBOX_TOKEN}}" yarn test:e2e
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
26 changes: 23 additions & 3 deletions e2e/generateTestData.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ const fg = require('fast-glob');
const catalogPaths = fg.globSync('**/datasets/*.mdx');
const storyPaths = fg.globSync('**/stories/*.mdx');
const catalogNames = [];
const catalogNamesHidden = [];
const datasetIds = [];
const datasetIdsHidden = [];
const datasetIdDisableExplore = [];
const storyNames = [];
const storyNamesHidden = [];

for (const catalog of catalogPaths) {
const catalogData = matter.read(catalog).data;
Expand All @@ -17,18 +20,35 @@ for (const catalog of catalogPaths) {
if(catalogData['disableExplore'] == true) {
datasetIdDisableExplore.push(catalogData['id'])
}
if(catalogData['isHidden'] == true) {
catalogNamesHidden.push(catalogData['name'])
datasetIdsHidden.push(catalogData['id'])
}
}

const catalogNamesVisible = catalogNames.filter(item => !catalogNamesHidden.includes(item));
const datasetIdsVisible = datasetIds.filter(item => !datasetIdsHidden.includes(item));

for (const story of storyPaths) {
const storyData = matter.read(story).data;
storyNames.push(storyData['name']);
storyNames.push(storyData['name']);
if(storyData['isHidden'] == true) {
storyNamesHidden.push(storyData['name'])
}
}
const storyNamesVisible = storyNames.filter(item => !storyNamesHidden.includes(item));

const testDataJson = {
catalogs: catalogNames,
catalogsHidden: catalogNamesHidden,
catalogsVisible: catalogNamesVisible,
datasetIds: datasetIds,
datasetsIdsDisabled: datasetIdDisableExplore,
datasetIdsHidden: datasetIdsHidden,
datasetIdsVisible: datasetIdsVisible,
stories: storyNames,
disabledDatasets: datasetIdDisableExplore,
storiesHidden: storyNamesHidden,
storiesVisible: storyNamesVisible,
};

fs.writeFile(
Expand All @@ -44,4 +64,4 @@ fs.writeFile(
console.info('new test data file generated');
}
}
);
);
30 changes: 26 additions & 4 deletions e2e/pages/aboutPage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { Locator, Page } from '@playwright/test';
import { Locator, Page, test } from '@playwright/test';

export default class AboutPage {
readonly page: Page;
readonly mainContent: Locator;
readonly aboutParagraph: Locator;
readonly partnersSection: Locator;
readonly partnerLink: Locator;

constructor(page: Page) {
this.page = page;
this.mainContent = this.page.getByRole('main');
this.aboutParagraph = this.page.getByText("The U.S. Greenhouse Gas Center (US GHG Center) is a multi-agency effort");
this.partnersSection = this.page.locator('div').filter({has: this.page.getByRole('heading', { level: 2, name: /Our Partners/i })});
this.partnerLink = this.partnersSection.getByRole('link').filter({has: this.page.locator('img')});
}
}

async getAllPartnerLinks() {
return await test.step('get all partner links', async() => {
return this.partnerLink.all();
})
}

async getPartnerName(partner: Locator) {
console.log(`looking at ${partner}`)
const partnerText = await test.step('getting text name for partner', async() => {
// await partner.scrollIntoViewIfNeeded();
const partnerText = await partner.innerText();
console.log(`found ${partnerText}`)
return partnerText
})
console.log(`found ${partnerText} here`)
return partnerText
}
}
2 changes: 1 addition & 1 deletion e2e/pages/analysisPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ export default class AnalysisPage {
this.datasetCheckbox.nth(index).click();
})
}
}
}
2 changes: 1 addition & 1 deletion e2e/pages/analysisResultsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export default class AnalysisResultsPage {
this.analysisCards = this.page.getByRole('article');
}

}
}
39 changes: 37 additions & 2 deletions e2e/pages/basePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,42 @@ import AboutPage from './aboutPage';
import AnalysisPage from './analysisPage';
import AnalysisResultsPage from './analysisResultsPage';
import CatalogPage from './catalogPage';
import ContactModal from './contactModal';
import DatasetPage from './datasetPage';
import DatasetSelectorComponent from './datasetSelectorComponent';
import DataToolkitPage from './dataToolkitPage';
import DisclaimerComponent from './disclaimerComponent';
import ExplorePage from './explorePage';
import FooterComponent from './footerComponent';
import HeaderComponent from './headerComponent';
import HomePage from './homePage';
import LearnPage from './learnPage';
import NewsPage from './newsPage';
import NotebookConnectModal from './notebookConnectModal';
import StoryPage from './storyPage';
import TopicsPage from './topicsPage';
import SubscribePage from './subscribePage';

export const test = base.extend<{
aboutPage: AboutPage;
analysisPage: AnalysisPage;
analysisResultsPage: AnalysisResultsPage;
catalogPage: CatalogPage;
contactModal: ContactModal;
datasetPage: DatasetPage;
datasetSelectorComponent: DatasetSelectorComponent;
dataToolkitPage: DataToolkitPage;
disclaimerComponent: DisclaimerComponent;
explorePage: ExplorePage;
footerComponent: FooterComponent;
headerComponent: HeaderComponent;
homePage: HomePage;
storyPage: StoryPage
learnPage: LearnPage;
newsPage: NewsPage;
storyPage: StoryPage;
topicsPage: TopicsPage;
subscribePage: SubscribePage;
notebookConnectModal: NotebookConnectModal;
}> ({
aboutPage: async ({page}, use) => {
await use(new AboutPage(page));
Expand All @@ -38,12 +52,18 @@ export const test = base.extend<{
catalogPage: async ({page}, use) => {
await use(new CatalogPage(page));
},
contactModal: async ({page}, use) => {
await use(new ContactModal(page));
},
datasetPage: async ({page}, use) => {
await use(new DatasetPage(page));
},
datasetSelectorComponent: async ({page}, use) => {
await use(new DatasetSelectorComponent(page));
},
dataToolkitPage: async ({page}, use) => {
await use(new DataToolkitPage(page));
},
disclaimerComponent: async ({page}, use) => {
await use(new DisclaimerComponent(page));
},
Expand All @@ -59,9 +79,24 @@ export const test = base.extend<{
homePage: async ({page}, use) => {
await use(new HomePage(page));
},
learnPage: async ({page}, use) => {
await use(new LearnPage(page));
},
newsPage: async({page}, use) => {
await use(new NewsPage(page))
},
notebookConnectModal: async({page}, use) => {
await use(new NotebookConnectModal(page))
},
storyPage: async ({page}, use) => {
await use(new StoryPage(page));
},
subscribePage: async ({page}, use) => {
await use(new SubscribePage(page));
},
topicsPage: async ({page}, use) => {
await use(new TopicsPage(page));
},
});

export const expect = test.expect;
export const expect = test.expect;
14 changes: 11 additions & 3 deletions e2e/pages/catalogPage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Locator, Page } from '@playwright/test';
import { Locator, Page, test } from '@playwright/test';

export default class CatalogPage {
readonly page: Page;
Expand All @@ -11,8 +11,16 @@ export default class CatalogPage {
constructor(page: Page) {
this.page = page;
this.mainContent = this.page.getByRole('main');
this.header = this.mainContent.getByRole('heading', {level: 1})
this.header = this.mainContent.getByRole('heading', {level: 1, name: /data catalog/i});
this.accessDataButton = this.page.getByRole('button', {name: /access data/i });
this.exploreDataButton = this.page.getByRole('button', {name: /explore data/i });
}
}

async clickCatalogCard(item: string) {
await test.step(`click on catalog card for ${item}`, async() => {
const catalogCard = this.mainContent.getByRole('article').getByRole('heading', {level: 3, name: item, exact: true}).first();
await catalogCard.scrollIntoViewIfNeeded();
await catalogCard.click({force: true});
})
}
}
11 changes: 11 additions & 0 deletions e2e/pages/contactModal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Locator, Page } from '@playwright/test';

export default class ContactModal {
readonly page: Page;
readonly header: Locator;

constructor(page: Page) {
this.page = page;
this.header = this.page.getByRole("heading", {level: 1, name: /contact us/i })
}
}
14 changes: 14 additions & 0 deletions e2e/pages/dataToolkitPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Locator, Page } from '@playwright/test';

export default class DataToolkitPage {
readonly page: Page;
readonly mainContent: Locator;
readonly header: Locator;


constructor(page: Page) {
this.page = page;
this.mainContent = this.page.getByRole('main');
this.header = this.mainContent.getByRole('heading', {level: 1, name: /accessing and exploring data/i })
}
}
12 changes: 10 additions & 2 deletions e2e/pages/datasetPage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Locator, Page } from '@playwright/test';
import { Locator, Page, test } from '@playwright/test';

export default class DatasetPage {
readonly page: Page;
readonly mainContent: Locator;
readonly header: Locator;
readonly exploreDataButton: Locator;
readonly analyzeDataButton: Locator;
readonly taxonomyLinkSection: Locator;


constructor(page: Page) {
Expand All @@ -14,5 +15,12 @@ export default class DatasetPage {
this.header = this.mainContent.getByRole('heading', { level: 1 })
this.exploreDataButton = this.page.getByRole('link', {name: /explore data/i} );
this.analyzeDataButton = this.page.getByRole('button', {name: /analyze data/i} );
this.taxonomyLinkSection = this.page.locator('section').filter({has: page.getByRole('heading', {name: /taxonomy/i , includeHidden: true})});
}
}

async getAllTaxonomyLinks() {
return await test.step('get all links in taxonomy section', async() => {
return this.taxonomyLinkSection.locator('dd').getByRole('link').all();
})
}
}
4 changes: 3 additions & 1 deletion e2e/pages/datasetSelectorComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ export default class DatasetSelectorComponent {
readonly page: Page;
readonly article: Locator;
readonly addToMapButton: Locator;
readonly header: Locator;
readonly noDatasetMessage: Locator;

constructor(page: Page) {
this.page = page;
this.article = this.page.getByRole('article');
this.addToMapButton = this.page.getByRole('button', {name: /add to map/i });
this.noDatasetMessage = this.page.getByText(/There are no datasets to show with the selected filters./i);
this.header = this.page.getByRole('heading', { level: 1, name: /data layers/i });
}

async addFirstDataset() {
Expand All @@ -19,4 +21,4 @@ export default class DatasetSelectorComponent {
await this.addToMapButton.click();
})
}
}
}
2 changes: 1 addition & 1 deletion e2e/pages/disclaimerComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ export default class DisclaimerComponent {
await this.acceptButton.click();
})
}
}
}
Loading

0 comments on commit 6f7e6d9

Please sign in to comment.