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

Tests - Start Page Object Model for Playwright #5204

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions tests/end2end/playwright/attribute-table.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check
import { test, expect } from '@playwright/test';
import { ProjectPage } from './pages/project';
import { gotoMap } from './globals';

test.describe('Attribute table', () => {
Expand All @@ -9,18 +10,17 @@ test.describe('Attribute table', () => {
});

test('Thumbnail class generate img with good path', async ({ page }) => {
await page.locator('a#button-attributeLayers').click();
// display form
//await page.locator('#button-edition').click();
await page.locator('#attribute-layer-list-table').locator('button[value=Les_quartiers_a_Montpellier]').click();
await expect(page.locator('#attribute-layer-table-Les_quartiers_a_Montpellier tbody tr')).toHaveCount(7);
const project = new ProjectPage(page, 'attribute_table');
const layerName = 'Les_quartiers_a_Montpellier';

await project.openAttributeTable(layerName);
await expect(project.attributeTableHtml(layerName).locator('tbody tr')).toHaveCount(7);
// mediaFile as stored in data-src attributes
const mediaFile = await page.locator('#attribute-layer-table-Les_quartiers_a_Montpellier img.data-attr-thumbnail').first().getAttribute('data-src');
const mediaFile = await project.attributeTableHtml(layerName).locator('img.data-attr-thumbnail').first().getAttribute('data-src');
// ensure src contain "dynamic" mediaFile
await expect(page.locator('#attribute-layer-table-Les_quartiers_a_Montpellier img.data-attr-thumbnail').first()).toHaveAttribute('src', new RegExp(mediaFile));
await expect(project.attributeTableHtml(layerName).locator('img.data-attr-thumbnail').first()).toHaveAttribute('src', new RegExp(mediaFile));
// ensure src contain getMedia and projet URL
await expect(page.locator('#attribute-layer-table-Les_quartiers_a_Montpellier img.data-attr-thumbnail').first()).toHaveAttribute('src', /getMedia\?repository=testsrepository&project=attribute_table&/);
await expect(project.attributeTableHtml(layerName).locator('img.data-attr-thumbnail').first()).toHaveAttribute('src', /getMedia\?repository=testsrepository&project=attribute_table&/);
});
});

Expand All @@ -37,8 +37,9 @@ test.describe('Attribute table data restricted to map extent', () => {
});

test('Data restriction, refresh button behaviour and export', async ({ page }) => {
await page.locator('a#button-attributeLayers').click();
await page.locator('#attribute-layer-list-table').locator('button[value=Les_quartiers_a_Montpellier]').click();
const project = new ProjectPage(page, 'attribute_table');
const layerName = 'Les_quartiers_a_Montpellier';
await project.openAttributeTable(layerName);

await expect(page.locator('.btn-refresh-table')).not.toHaveClass(/btn-warning/);

Expand All @@ -53,7 +54,7 @@ test.describe('Attribute table data restricted to map extent', () => {
// Refresh
await page.locator('.btn-refresh-table').click();

await expect(page.locator('#attribute-layer-table-Les_quartiers_a_Montpellier tbody tr')).toHaveCount(5);
await expect(project.attributeTableHtml(layerName).locator('tbody tr')).toHaveCount(5);

const getFeatureRequestPromise = page.waitForRequest(request => request.method() === 'POST' && request.postData() != null && request.postData()?.includes('GetFeature') === true);
// bbox in getFeature request for export
Expand Down
34 changes: 13 additions & 21 deletions tests/end2end/playwright/edition-form.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
import { test, expect } from '@playwright/test';
import { gotoMap } from './globals';
import {ProjectPage} from "./pages/project";

test.describe('Edition Form Validation', () => {

Expand All @@ -10,9 +11,8 @@ test.describe('Edition Form Validation', () => {
});

test('Input type number with range and step', async ({ page }) => {
// display form
await page.locator('#button-edition').click();
await page.locator('a#edition-draw').click();
const project = new ProjectPage(page, 'form_edition_all_field_type');
await project.openEditingFormWithLayer('form_edition_all_fields_types');

// ensure input attributes match with field config defined in project
await expect(page.locator('#jforms_view_edition input[name="integer_field"]')).toHaveAttribute('type', 'number')
Expand All @@ -24,22 +24,17 @@ test.describe('Edition Form Validation', () => {
await page.locator('#jforms_view_edition input[name="integer_field"]').fill('50');

// submit form
await page.locator('#jforms_view_edition__submit_submit').click();
// will close & show message
await expect(page.locator('#edition-form-container')).toBeHidden();
await expect(page.locator('#lizmap-edition-message')).toBeVisible();
await project.editingSubmitForm()
})

test('Boolean nullable w/ value map', async ({ page }) => {

let editFeatureRequestPromise = page.waitForResponse(response => response.url().includes('editFeature'));

await page.locator('#button-edition').click();
await page.locator('#edition-layer').selectOption({ label: 'many_bool_formats' });
await page.locator('#edition-draw').click();
await page.locator('#jforms_view_edition_liz_future_action').selectOption('edit');
const project = new ProjectPage(page, 'form_edition_all_field_type');
await project.openEditingFormWithLayer('many_bool_formats');
await page.getByLabel('bool_simple_null_vm').selectOption('t');
await page.locator('#jforms_view_edition__submit_submit').click();
await project.editingSubmitForm('edit');

await editFeatureRequestPromise;

Expand All @@ -49,7 +44,7 @@ test.describe('Edition Form Validation', () => {
await expect(page.getByLabel('bool_simple_null_vm')).toHaveValue('t');

await page.getByLabel('bool_simple_null_vm').selectOption('');
await page.locator('#jforms_view_edition__submit_submit').click();
await project.editingSubmitForm('edit');

await editFeatureRequestPromise;

Expand All @@ -68,8 +63,8 @@ test.describe('Edition Form Validation', () => {
});
});

// display form
await page.locator('#button-edition').click();
const project = new ProjectPage(page, 'form_edition_all_field_type');
await project.buttonEditing.click();
await page.locator('a#edition-draw').click();

// message
Expand Down Expand Up @@ -522,12 +517,9 @@ test.describe('Text widget in a form', () => {

let getFeatureInfoRequestPromise = page.waitForRequest(request => request.method() === 'POST' && request.postData()?.includes('GetFeatureInfo') === true);

await page.locator('#newOlMap').click({
position: {
x: 354,
y: 370
}
});
const project = new ProjectPage(page, 'text_widget');

await project.clickOnMap(354, 370);

await getFeatureInfoRequestPromise;

Expand Down
10 changes: 4 additions & 6 deletions tests/end2end/playwright/media.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// @ts-check
import { test, expect } from '@playwright/test';
import { gotoMap } from './globals';
import {ProjectPage} from "./pages/project";

test.describe('Media', () => {
test('Tests media are deleted', async ({ page }) => {
Expand All @@ -17,14 +18,11 @@ test.describe('Media', () => {
await expect(response).toBeOK();

// Open the attribute table
const url = '/index.php/view/map?repository=testsrepository&project=form_edition_all_field_type';
await gotoMap(url, page);

await page.locator('#button-attributeLayers').click();

const project = new ProjectPage(page, 'form_edition_all_field_type');
await project.open();
let getFeatureRequestPromise = page.waitForRequest(request => request.method() === 'POST' && request.postData()?.includes('GetFeature') === true);

await page.locator('#attribute-layer-list button[value="form_edition_upload"]').click();
await project.openAttributeTable('form_edition_upload');
await getFeatureRequestPromise;

await page.getByRole('row', { name: '2 text_file_mandatory' }).getByRole('button').nth(2);
Expand Down
163 changes: 163 additions & 0 deletions tests/end2end/playwright/pages/project.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// @ts-check
import {expect, Locator, Page} from '@playwright/test';
import { gotoMap } from '../globals';

export class ProjectPage {
/** @type {Page} */
page;

// Metadata
/**
* Project name metadata
* @type {string}
*/
project;
/**
* Repository name metadata
* @type {string}
*/
repository;

// Menu
/**
* Layer switcher menu
* @type {Locator}
*/
switcher;
/**
* Editing menu
* @type {Locator}
*/
buttonEditing;

// Docks
/**
* Attribute table dock
* @type {Locator}
*/
attributeTable;
/**
* Main left dock
* @type {Locator}
*/
dock;
/**
* Right dock
* @type {Locator}
*/
rightDock;
/**
* Bottom dock
* @type {Locator}
*/
bottomDock;
/**
* Mini dock
* @type {Locator}
*/
miniDock;
/**
* Top search bar
* @type {Locator}
*/
search;

// Messages
/**
* Foreground message bar
* @type {Locator}
*/
warningMessage;

/**
* Attribute table for the given layer name
* @param {string} name Name of the layer
* @returns {Locator}
*/
attributeTableHtml = (name) =>
this.page.locator(`#attribute-layer-table-${name}`);

/**
* Constructor
* @param {Page} page The playwright page
* @param {string} project The project name
* @param {string} repository The repository name, default to testsrepository
*/
constructor(page, project, repository = 'testsrepository') {
this.page = page;
this.project = project;
this.repository = repository;
this.dock = page.locator('#dock');
this.rightDock = page.locator('#right-dock');
this.bottomDock = page.locator('#bottom-dock');
this.miniDock = page.locator('#mini-dock-content');
this.warningMessage = page.locator('#lizmap-warning-message');
this.search = page.locator('#search-query');
this.switcher = page.locator('#button-switcher');
this.buttonEditing = page.locator('#button-edition');
}

/**
* open function
* Open the URL for the given project and repository
*/
async open(){
await gotoMap(`/index.php/view/map?repository=${this.repository}&project=${this.project}`, this.page);
}

/**
* openAttributeTable function
* Open the attribute table for the given layer
* @param {string} layer Name of the layer
* @param {boolean} maximise If the attribute table must be maximised
*/
async openAttributeTable(layer, maximise = false){
await this.page.locator('a#button-attributeLayers').click();
if (maximise) {
await this.page.getByRole('button', { name: 'Maximize' }).click();
}
await this.page.locator('#attribute-layer-list-table').locator(`button[value=${layer}]`).click();
}

/**
* editingSubmitForm function
* Submit the form
* @param {string} futureAction The action to do after submit : can be close/create/edit.
*/
async editingSubmitForm(futureAction = 'close'){
await this.page.locator('#jforms_view_edition_liz_future_action').selectOption(futureAction);
await this.page.locator('#jforms_view_edition__submit_submit').click();
if (futureAction === 'close'){
await expect(this.page.locator('#edition-form-container')).toBeHidden();
} else {
await expect(this.page.locator('#edition-form-container')).toBeVisible();
}
await expect(this.page.locator('#lizmap-edition-message')).toBeVisible();
}

/**
* openEditingFormWithLayer function
* Open the editing panel with the given layer name form
* @param {string} layer Name of the layer
*/
async openEditingFormWithLayer(layer){
await this.buttonEditing.click();
await this.page.locator('#edition-layer').selectOption({ label: layer });
await this.page.locator('a#edition-draw').click();
}

/**
* clickOnMap function
* Click on the map at the given position
* @param {number} x Position X on the map
* @param {number} y Position Y on the map
*/
async clickOnMap(x, y){
await this.page.locator('#newOlMap').click({
position: {
x: x,
y: y
}
});
}
}
16 changes: 7 additions & 9 deletions tests/end2end/playwright/project_load_warning.spec.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
// @ts-check
import { test, expect } from '@playwright/test';
import { gotoMap } from './globals';
import { ProjectPage } from './pages/project';

test.describe('Project warnings in CFG as admin', () => {
test.use({ storageState: 'playwright/.auth/admin.json' });

test('Visit map with a warning', async ({ page }) => {
const url = '/index.php/view/map?repository=testsrepository&project=project_cfg_warnings';
await gotoMap(url, page)

await expect(page.locator("#lizmap-warning-message")).toBeVisible();
const project = new ProjectPage(page, 'project_cfg_warnings');
await project.open();
await expect(project.warningMessage).toBeVisible();
});

});

test.describe('Project warnings in CFG as anonymous', () => {

test('Visit map without a warning', async ({ page }) => {
const url = '/index.php/view/map?repository=testsrepository&project=project_cfg_warnings';
await gotoMap(url, page)

await expect(page.locator("#lizmap-warning-message")).toHaveCount(0);
const project = new ProjectPage(page, 'project_cfg_warnings');
await project.open();
await expect(project.warningMessage).toHaveCount(0);
});

});
Loading
Loading