Skip to content

Commit

Permalink
Add end to end tests for app store
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexAndBear committed Sep 26, 2024
1 parent 4504b08 commit 10977a6
Show file tree
Hide file tree
Showing 8 changed files with 294 additions and 3 deletions.
4 changes: 2 additions & 2 deletions packages/web-app-app-store/src/views/AppDetails.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<template>
<div class="app-details oc-card oc-card-default oc-card-rounded">
<div class="oc-p-xs">
<router-link :to="{ name: `${APPID}-list` }" class="oc-flex oc-flex-middle">
<router-link :to="{ name: `${APPID}-list` }" class="oc-flex oc-flex-middle app-details-back">
<oc-icon name="arrow-left-s" fill-type="line" />
<span v-text="$gettext('Back to list')" />
</router-link>
</div>
<app-image-gallery :app="app" :show-pagination="true" />
<div class="app-content oc-card-body oc-p">
<div class="oc-flex oc-flex-middle">
<h2 class="oc-my-s oc-text-truncate">{{ app.name }}</h2>
<h2 class="oc-my-s oc-text-truncate app-details-title">{{ app.name }}</h2>
<span class="oc-ml-s oc-text-muted oc-text-small oc-mt-s">
v{{ app.mostRecentVersion.version }}
</span>
Expand Down
2 changes: 1 addition & 1 deletion packages/web-app-app-store/src/views/AppList.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="app-list oc-mb-m">
<h2 class="oc-mt-rm">
<h2 class="oc-mt-rm app-list-headline">
{{ $gettext('App Store') }}
<app-contextual-helper />
</h2>
Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/cucumber/features/app-store/details.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Feature: details

Scenario: app store can be discovered
When "Admin" logs in
And "Admin" navigates to the app store
Then "Admin" should see the app store
When "Admin" clicks on the app "Development boilerplate"
Then "Admin" should see the app details of "Development boilerplate"
And "Admin" downloads app version "0.1.0"
When "Admin" navigates back to the app store overview
Then "Admin" should see the app store
And "Admin" downloads the latest version of the app "Development boilerplate"
And "Admin" logs out
29 changes: 29 additions & 0 deletions tests/e2e/cucumber/features/app-store/discovery.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Feature: discovery

Scenario: app store can be discovered
When "Admin" logs in
And "Admin" navigates to the app store
Then "Admin" should see the app store
And "Admin" should see the following apps
| app |
| Draw.io |
| JSON Viewer |
| Unzip |
When "Admin" enters the search term "draw"
Then "Admin" should see the following apps
| app |
| Draw.io |
When "Admin" clicks on the tag "viewer" of the app "Draw.io"
Then "Admin" should see the following apps
| app |
| JSON Viewer |
| Draw.io |
When "Admin" clicks on the app "JSON Viewer"
Then "Admin" should see the app details of "JSON Viewer"
When "Admin" clicks on the tag "viewer"
Then "Admin" should see the app store
Then "Admin" should see the following apps
| app |
| JSON Viewer |
| Draw.io |
And "Admin" logs out
105 changes: 105 additions & 0 deletions tests/e2e/cucumber/steps/ui/appStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { DataTable, Then, When } from '@cucumber/cucumber'
import { World } from '../../environment'
import { objects } from '../../../support'
import { expect } from '@playwright/test'

When(
'{string} navigates to the app store',
async function (this: World, stepUser: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.openAppStore()
}
)

Then(
'{string} should see the app store',
async function (this: World, stepUser: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.waitForAppStoreIsVisible()
}
)

Then(
'{string} should see the following apps(s)',
async function (this: World, stepUser: string, stepTable: DataTable): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
const apps = await pageObject.getAppsList()
for (const { app } of stepTable.hashes()) {
expect(apps).toContain(app)
}
}
)

When(
'{string} enters the search term {string}',
async function (this: World, stepUser: string, searchTerm: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.setSearchTerm(searchTerm)
}
)

When(
'{string} clicks on the tag {string} of the app {string}',
async function (this: World, stepUser: string, tag: string, app: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.selectAppTag({ tag, app })
}
)

When(
'{string} clicks on the tag {string}',
async function (this: World, stepUser: string, tag: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.selectTag(tag)
}
)

When(
'{string} clicks on the app {string}',
async function (this: World, stepUser: string, app: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.selectApp(app)
}
)

Then(
'{string} should see the app details of {string}',
async function (this: World, stepUser: string, app: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.waitForAppDetailsIsVisible(app)
}
)

Then(
'{string} downloads app version {string}',
async function (this: World, stepUser: string, version: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
expect(await pageObject.downloadAppVersion(version)).toContain(version)
}
)
Then(
'{string} downloads the latest version of the app {string}',
async function (this: World, stepUser: string, app: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
expect(await pageObject.downloadApp(app)).toBeDefined()
}
)

When(
'{string} navigates back to the app store overview',
async function (this: World, stepUser: string): Promise<void> {
const { page } = this.actorsEnvironment.getActor({ key: stepUser })
const pageObject = new objects.appStore.AppStore({ page })
await pageObject.navigateToAppStoreOverview()
}
)
89 changes: 89 additions & 0 deletions tests/e2e/support/objects/app-store/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Download, Page } from '@playwright/test'
import util from 'util'

const selectors = {
appLoadingSpinner: '#app-loading-spinner',
appSwitcherButton: '#_appSwitcherButton',
appStoreMenuButton: 'data-test-id=app.app-store.menuItem',
downloadButton: '//a[contains(., "%s")]/ancestor::li//button[.//span[text()="Download"]]',
downloadVersionButton: '//tr[@data-item-id="%s"]//button[.//span[text()="Download"]]',
appStoreHeadline: '.app-list-headline',
appTileTitle: '.app-tile-title',
selectAppTitle: '//a[contains(.,"%s")]',
appDetailsBack: '.app-details-back',
appDetailsTitle: '//h2[contains(@class, "app-details-title")][text()="%s"]',
appsFilter: '#apps-filter',
tag: '//button[contains(@class,"oc-tag")][span[text()="%s"]]',
appTag: '//a[contains(.,"%s")]/following::button[contains(@class,"oc-tag")][span[text()="%s"]]'
}

export const openAppStore = async (args: { page: Page }): Promise<void> => {
const { page } = args
await page.locator(selectors.appSwitcherButton).click()
await page.locator(selectors.appStoreMenuButton).click()
await page.locator(selectors.appLoadingSpinner).waitFor({ state: 'detached' })
}
export const navigateToAppStoreOverview = async (args: { page: Page }): Promise<void> => {
const { page } = args
await page.locator(selectors.appDetailsBack).click()
await page.locator(selectors.appDetailsBack).waitFor({ state: 'detached' })
}

export const waitForAppStoreIsVisible = (args: { page: Page }): Promise<void> => {
const { page } = args
return page.locator(selectors.appStoreHeadline).waitFor()
}

export const getAppsList = (args: { page: Page }): Promise<string[]> => {
const { page } = args
return page.locator(selectors.appTileTitle).allTextContents()
}

export const setSearchTerm = (args: { page: Page; searchTerm: string }): Promise<void> => {
const { page, searchTerm } = args
return page.locator(selectors.appsFilter).fill(searchTerm)
}

export const selectAppTag = (args: { page: Page; app: string; tag: string }): Promise<void> => {
const { page, app, tag } = args
return page.locator(util.format(selectors.appTag, app, tag)).click()
}
export const selectTag = (args: { page: Page; tag: string }): Promise<void> => {
const { page, tag } = args
return page.locator(util.format(selectors.tag, tag)).click()
}

export const selectApp = (args: { page: Page; app: string }): Promise<void> => {
const { page, app } = args
return page.locator(util.format(selectors.selectAppTitle, app)).click()
}

export const waitForAppDetailsIsVisible = (args: { page: Page; app }): Promise<void> => {
const { page, app } = args
return page.locator(util.format(selectors.appDetailsTitle, app)).waitFor()
}

export const downloadAppVersion = async (args: {
page: Page
version: string
}): Promise<string> => {
const { page, version } = args
const [download] = await Promise.all([
// Start waiting for the download CONTINUE HERE
page.waitForEvent('download'),
// Perform the action that initiates download
page.locator(util.format(selectors.downloadVersionButton, version)).click()
])

return download.suggestedFilename()
}

export const downloadApp = async (args: { page: Page; app: string }): Promise<Download> => {
const { page, app } = args
const [download] = await Promise.all([
page.waitForEvent('download'),
page.locator(util.format(selectors.downloadButton, app)).click()
])

return download
}
54 changes: 54 additions & 0 deletions tests/e2e/support/objects/app-store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Download, Page } from '@playwright/test'
import * as po from './actions'

export class AppStore {
#page: Page

constructor({ page }: { page: Page }) {
this.#page = page
}

async openAppStore(): Promise<void> {
await po.openAppStore({ page: this.#page })
}

async waitForAppStoreIsVisible(): Promise<void> {
return po.waitForAppStoreIsVisible({ page: this.#page })
}

async getAppsList(): Promise<string[]> {
return po.getAppsList({ page: this.#page })
}

async setSearchTerm(searchTerm: string): Promise<void> {
return po.setSearchTerm({ page: this.#page, searchTerm })
}

async selectAppTag({ app, tag }: { app: string; tag: string }): Promise<void> {
return po.selectAppTag({ page: this.#page, app, tag })
}

async selectTag(tag): Promise<void> {
return po.selectTag({ page: this.#page, tag })
}

async selectApp(app: string): Promise<void> {
return po.selectApp({ page: this.#page, app })
}

async waitForAppDetailsIsVisible(app: string): Promise<void> {
return po.waitForAppDetailsIsVisible({ page: this.#page, app })
}

async downloadApp(app: string): Promise<Download> {
return po.downloadApp({ page: this.#page, app })
}

async downloadAppVersion(version: string): Promise<string> {
return po.downloadAppVersion({ page: this.#page, version })
}

async navigateToAppStoreOverview(): Promise<void> {
await po.navigateToAppStoreOverview({ page: this.#page })
}
}
1 change: 1 addition & 0 deletions tests/e2e/support/objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * as applicationAdminSettings from './app-admin-settings'
export * as runtime from './runtime'
export * as account from './account'
export * as urlNavigation from './url-navigation'
export * as appStore from './app-store'

0 comments on commit 10977a6

Please sign in to comment.