Ported more tests to playwright. (#6214)

* Ported more tests to playwright.

* Added language test.

* Fixed failing tests. Moved another test.

* Removed frontend tests.

* Splitted runners.

* Fixed runners.

* Split up into the different browser environments.

* Added github reporter.

* Added change user color test.
This commit is contained in:
SamTV12345 2024-03-12 17:45:47 +01:00 committed by GitHub
parent 19ee8c2afa
commit 078324c0d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 1702 additions and 1229 deletions

View file

@ -1,6 +1,6 @@
import {Frame, Locator, Page} from "@playwright/test";
import {MapArrayType} from "../../../node/types/MapType";
import {randomInt} from "node:crypto";
import {randomUUID} from "node:crypto";
export const getPadOuter = async (page: Page): Promise<Frame> => {
return page.frame('ace_outer')!;
@ -115,7 +115,7 @@ export const appendQueryParams = async (page: Page, queryParameters: MapArrayTyp
export const goToNewPad = async (page: Page) => {
// create a new pad before each test run
const padId = "FRONTEND_TESTS"+randomInt(0, 1000);
const padId = "FRONTEND_TESTS"+randomUUID();
await page.goto('http://localhost:9001/p/'+padId);
await page.waitForSelector('iframe[name="ace_outer"]');
return padId;
@ -128,6 +128,8 @@ export const goToPad = async (page: Page, padId: string) => {
export const clearPadContent = async (page: Page) => {
const body = await getPadBody(page);
await body.click();
await page.keyboard.down('Control');
await page.keyboard.press('A');
await page.keyboard.up('Control');

View file

@ -0,0 +1,20 @@
import {Page} from "@playwright/test";
/**
* Sets the src-attribute of the main iframe to the timeslider
* In case a revision is given, sets the timeslider to this specific revision.
* Defaults to going to the last revision.
* It waits until the timer is filled with date and time, because it's one of the
* last things that happen during timeslider load
*
* @param page
* @param {number} [revision] the optional revision
* @returns {Promise}
* @todo for some reason this does only work the first time, you cannot
* goto rev 0 and then via the same method to rev 5. Use buttons instead
*/
export const gotoTimeslider = async (page: Page, revision: number): Promise<any> => {
let revisionString = Number.isInteger(revision) ? `#${revision}` : '';
await page.goto(`${page.url()}/timeslider${revisionString}`);
await page.waitForSelector('#timer')
};

View file

@ -0,0 +1,103 @@
import {expect, test} from "@playwright/test";
import {goToNewPad, sendChatMessage, showChat} from "../helper/padHelper";
test.beforeEach(async ({page}) => {
await goToNewPad(page);
})
test.describe('change user color', function () {
test('Color picker matches original color and remembers the user color after a refresh',
async function ({page}) {
// click on the settings button to make settings visible
let $userButton = page.locator('.buttonicon-showusers');
await $userButton.click()
let $userSwatch = page.locator('#myswatch');
await $userSwatch.click()
// Change the color value of the Farbtastic color picker
const $colorPickerSave = page.locator('#mycolorpickersave');
let $colorPickerPreview = page.locator('#mycolorpickerpreview');
// Same color represented in two different ways
const testColorHash = '#abcdef';
const testColorRGB = 'rgb(171, 205, 239)';
// Check that the color picker matches the automatically assigned random color on the swatch.
// NOTE: This has a tiny chance of creating a false positive for passing in the
// off-chance the randomly assigned color is the same as the test color.
expect(await $colorPickerPreview.getAttribute('style')).toContain(await $userSwatch.getAttribute('style'));
// The swatch updates as the test color is picked.
await page.evaluate((testRGBColor) => {
document.getElementById('mycolorpickerpreview')!.style.backgroundColor = testRGBColor;
}, testColorRGB
)
await $colorPickerSave.click();
// give it a second to save the color on the server side
await page.waitForTimeout(1000)
// get a new pad, but don't clear the cookies
await goToNewPad(page)
// click on the settings button to make settings visible
await $userButton.click()
await $userSwatch.click()
expect(await $colorPickerPreview.getAttribute('style')).toContain(await $userSwatch.getAttribute('style'));
});
test('Own user color is shown when you enter a chat', async function ({page}) {
const colorOption = page.locator('#options-colorscheck');
if (!(await colorOption.isChecked())) {
await colorOption.check();
}
// click on the settings button to make settings visible
const $userButton = page.locator('.buttonicon-showusers');
await $userButton.click()
const $userSwatch = page.locator('#myswatch');
await $userSwatch.click()
const $colorPickerSave = page.locator('#mycolorpickersave');
// Same color represented in two different ways
const testColorHash = '#abcdef';
const testColorRGB = 'rgb(171, 205, 239)';
// The swatch updates as the test color is picked.
await page.evaluate((testRGBColor) => {
document.getElementById('mycolorpickerpreview')!.style.backgroundColor = testRGBColor;
}, testColorRGB
)
await $colorPickerSave.click();
// click on the chat button to make chat visible
await showChat(page)
await sendChatMessage(page, 'O hi');
// wait until the chat message shows up
const chatP = page.locator('#chattext').locator('p')
const chatText = await chatP.innerText();
expect(chatText).toContain('O hi');
const color = await chatP.evaluate((el) => {
return window.getComputedStyle(el).getPropertyValue('background-color');
}, chatText);
expect(color).toBe(testColorRGB);
});
});

View file

@ -63,8 +63,7 @@ test.describe('embed links', function () {
// create a new pad before each test run
await goToNewPad(page);
})
test.describe('the share link', function () {
test('is the actual pad url', async function ({page}) {
test('the share link is the actual pad url', async function ({page}) {
const shareButton = page.locator('.buttonicon-embed')
// open share dropdown
@ -75,10 +74,8 @@ test.describe('embed links', function () {
const padURL = page.url();
expect(shareLink).toBe(padURL);
});
});
test.describe('the embed as iframe code', function () {
test('is an iframe with the the correct url parameters and correct size', async function ({page}) {
test('is an iframe with the the correct url parameters and correct size', async function ({page}) {
const shareButton = page.locator('.buttonicon-embed')
await shareButton.click()
@ -89,7 +86,6 @@ test.describe('embed links', function () {
await checkiFrameCode(embedCode, false, page);
});
});
});
test.describe('when read only option is set', function () {
@ -98,8 +94,7 @@ test.describe('embed links', function () {
await goToNewPad(page);
})
test.describe('the share link', function () {
test('shows a read only url', async function ({page}) {
test('the share link shows a read only url', async function ({page}) {
// open share dropdown
const shareButton = page.locator('.buttonicon-embed')
@ -115,10 +110,8 @@ test.describe('embed links', function () {
const containsReadOnlyLink = shareLink.indexOf('r.') > 0;
expect(containsReadOnlyLink).toBe(true);
});
});
test.describe('the embed as iframe code', function () {
test('is an iframe with the the correct url parameters and correct size', async function ({page}) {
test('the embed as iframe code is an iframe with the the correct url parameters and correct size', async function ({page}) {
// open share dropdown
@ -139,8 +132,5 @@ test.describe('embed links', function () {
await checkiFrameCode(embedCode, true, page);
});
});
})
})

View file

@ -0,0 +1,39 @@
import {expect, test} from "@playwright/test";
import {getPadBody, goToNewPad} from "../helper/padHelper";
import {showSettings} from "../helper/settingsHelper";
test.beforeEach(async ({ page })=>{
// create a new pad before each test run
await goToNewPad(page);
})
test.describe('font select', function () {
// create a new pad before each test run
test('makes text RobotoMono', async function ({page}) {
// click on the settings button to make settings visible
await showSettings(page);
// get the font menu and RobotoMono option
const viewFontMenu = page.locator('#viewfontmenu');
// select RobotoMono and fire change event
// $RobotoMonooption.attr('selected','selected');
// commenting out above will break safari test
const dropdown = page.locator('.dropdowns-container .dropdown-line .current').nth(0)
await dropdown.click()
await page.locator('li:text("RobotoMono")').click()
await viewFontMenu.dispatchEvent('change');
const padBody = await getPadBody(page)
const color = await padBody.evaluate((e) => {
return window.getComputedStyle(e).getPropertyValue("font-family")
})
// check if font changed to RobotoMono
const containsStr = color.toLowerCase().indexOf('robotomono');
expect(containsStr).not.toBe(-1);
});
});

View file

@ -25,7 +25,7 @@ test.describe('indentation button', function () {
const padBody = await getPadBody(page);
await page.locator('.buttonicon-indent').click()
const uls = padBody.locator('div').first().locator('ul li')
const uls = padBody.locator('div').first().locator('ul')
await expect(uls).toHaveCount(1);
});

View file

@ -0,0 +1,56 @@
'use strict';
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('height regression after ace.js refactoring', function () {
test('clientHeight should equal scrollHeight with few lines', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
const iframe = page.locator('iframe').first()
const scrollHeight = await iframe.evaluate((element) => {
return element.scrollHeight;
})
const clientHeight = await iframe.evaluate((element) => {
return element.clientHeight;
})
expect(clientHeight).toEqual(scrollHeight);
});
test('client height should be less than scrollHeight with many lines', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
await writeToPad(page,'Test line\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
const iframe = page.locator('iframe').first()
const scrollHeight = await iframe.evaluate((element) => {
return element.scrollHeight;
})
const clientHeight = await iframe.evaluate((element) => {
return element.clientHeight;
})
// Need to poll because the heights take some time to settle.
expect(clientHeight).toBeLessThanOrEqual(scrollHeight);
});
});

View file

@ -0,0 +1,65 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('italic some text', function () {
test('makes text italic using button', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
await $firstTextElement.click()
await writeToPad(page, 'Foo')
// select this text element
await padBody.click()
await page.keyboard.press('Control+A');
// get the bold button and click it
const $boldButton = page.locator('.buttonicon-italic');
await $boldButton.click();
// ace creates a new dom element when you press a button, just get the first text element again
const $newFirstTextElement = padBody.locator('div').first();
// is there a <i> element now?
// expect it to be italic
await expect($newFirstTextElement.locator('i')).toHaveCount(1);
// make sure the text hasn't changed
expect(await $newFirstTextElement.textContent()).toEqual(await $firstTextElement.textContent());
});
test('makes text italic using keypress', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await writeToPad(page, 'Foo')
await page.keyboard.press('Control+A');
await page.keyboard.press('Control+I');
// ace creates a new dom element when you press a button, just get the first text element again
const $newFirstTextElement = padBody.locator('div').first();
// is there a <i> element now?
// expect it to be italic
await expect($newFirstTextElement.locator('i')).toHaveCount(1);
// make sure the text hasn't changed
expect(await $newFirstTextElement.textContent()).toBe(await $firstTextElement.textContent());
});
});

View file

@ -0,0 +1,88 @@
import {expect, test} from "@playwright/test";
import {getPadBody, goToNewPad} from "../helper/padHelper";
import {showSettings} from "../helper/settingsHelper";
test.beforeEach(async ({ page, browser })=>{
const context = await browser.newContext()
await context.clearCookies()
await goToNewPad(page);
})
test.describe('Language select and change', function () {
// Destroy language cookies
test('makes text german', async function ({page}) {
// click on the settings button to make settings visible
await showSettings(page)
// click the language button
const languageDropDown = page.locator('.nice-select').nth(1)
await languageDropDown.click()
await page.locator('.nice-select').locator('[data-value=de]').click()
await expect(languageDropDown.locator('.current')).toHaveText('Deutsch')
// select german
await page.locator('.buttonicon-bold').evaluate((el) => el.parentElement!.title === 'Fett (Strg-B)');
});
test('makes text English', async function ({page}) {
await showSettings(page)
// click the language button
await page.locator('.nice-select').nth(1).locator('.current').click()
await page.locator('.nice-select').locator('[data-value=de]').click()
// select german
await page.locator('.buttonicon-bold').evaluate((el) => el.parentElement!.title === 'Fett (Strg-B)');
// change to english
await page.locator('.nice-select').nth(1).locator('.current').click()
await page.locator('.nice-select').locator('[data-value=en]').click()
// check if the language is now English
await page.locator('.buttonicon-bold').evaluate((el) => el.parentElement!.title !== 'Fett (Strg-B)');
});
test('changes direction when picking an rtl lang', async function ({page}) {
await showSettings(page)
// click the language button
await page.locator('.nice-select').nth(1).locator('.current').click()
await page.locator('.nice-select').locator('[data-value=de]').click()
// select german
await page.locator('.buttonicon-bold').evaluate((el) => el.parentElement!.title === 'Fett (Strg-B)');
// click the language button
await page.locator('.nice-select').nth(1).locator('.current').click()
// select arabic
// $languageoption.attr('selected','selected'); // Breaks the test..
await page.locator('.nice-select').locator('[data-value=ar]').click()
await page.waitForSelector('html[dir="rtl"]')
});
test('changes direction when picking an ltr lang', async function ({page}) {
await showSettings(page)
// change to english
const languageDropDown = page.locator('.nice-select').nth(1)
await languageDropDown.locator('.current').click()
await languageDropDown.locator('[data-value=en]').click()
await expect(languageDropDown.locator('.current')).toHaveText('English')
// check if the language is now English
await page.locator('.buttonicon-bold').evaluate((el) => el.parentElement!.title !== 'Fett (Strg-B)');
await page.waitForSelector('html[dir="ltr"]')
});
});

View file

@ -0,0 +1,109 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('ordered_list.js', function () {
test('issue #4748 keeps numbers increment on OL', async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
await writeToPad(page, 'Line 1')
await page.keyboard.press('Enter')
await writeToPad(page, 'Line 2')
const $insertorderedlistButton = page.locator('.buttonicon-insertorderedlist')
await padBody.locator('div').first().selectText()
await $insertorderedlistButton.first().click();
const secondLine = padBody.locator('div').nth(1)
await secondLine.selectText()
await $insertorderedlistButton.click();
expect(await secondLine.locator('ol').getAttribute('start')).toEqual('2');
});
test('issue #1125 keeps the numbered list on enter for the new line', async function ({page}) {
// EMULATES PASTING INTO A PAD
const padBody = await getPadBody(page);
await clearPadContent(page)
await expect(padBody.locator('div')).toHaveCount(1)
const $insertorderedlistButton = page.locator('.buttonicon-insertorderedlist')
await $insertorderedlistButton.click();
// type a bit, make a line break and type again
const firstTextElement = padBody.locator('div').first()
await firstTextElement.click()
await writeToPad(page, 'line 1')
await page.keyboard.press('Enter')
await writeToPad(page, 'line 2')
await page.keyboard.press('Enter')
await expect(padBody.locator('div span').nth(1)).toHaveText('line 2');
const $newSecondLine = padBody.locator('div').nth(1)
expect(await $newSecondLine.locator('ol li').count()).toEqual(1);
await expect($newSecondLine.locator('ol li').nth(0)).toHaveText('line 2');
const hasLineNumber = await $newSecondLine.locator('ol').getAttribute('start');
// This doesn't work because pasting in content doesn't work
expect(Number(hasLineNumber)).toBe(2);
});
});
test.describe('Pressing Tab in an OL increases and decreases indentation', function () {
test('indent and de-indent list item with keypress', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await $firstTextElement.selectText()
const $insertorderedlistButton = page.locator('.buttonicon-insertorderedlist')
await $insertorderedlistButton.click()
await page.keyboard.press('Tab')
await expect(padBody.locator('div').first().locator('.list-number2')).toHaveCount(1)
await page.keyboard.press('Shift+Tab')
await expect(padBody.locator('div').first().locator('.list-number1')).toHaveCount(1)
});
});
test.describe('Pressing indent/outdent button in an OL increases and ' +
'decreases indentation and bullet / ol formatting', function () {
test('indent and de-indent list item with indent button', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await $firstTextElement.selectText()
const $insertorderedlistButton = page.locator('.buttonicon-insertorderedlist')
await $insertorderedlistButton.click()
const $indentButton = page.locator('.buttonicon-indent')
await $indentButton.dblclick() // make it indented twice
const outdentButton = page.locator('.buttonicon-outdent')
await expect(padBody.locator('div').first().locator('.list-number3')).toHaveCount(1)
await outdentButton.click(); // make it deindented to 1
await expect(padBody.locator('div').first().locator('.list-number2')).toHaveCount(1)
});
});

View file

@ -0,0 +1,65 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('undo button then redo button', function () {
test('redo some typing with button', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element inside the editable space
const $firstTextElement = padBody.locator('div span').first();
const originalValue = await $firstTextElement.textContent(); // get the original value
const newString = 'Foo';
await $firstTextElement.focus()
expect(await $firstTextElement.textContent()).toContain(originalValue);
await padBody.click()
await clearPadContent(page)
await writeToPad(page, newString); // send line 1 to the pad
const modifiedValue = await $firstTextElement.textContent(); // get the modified value
expect(modifiedValue).not.toBe(originalValue); // expect the value to change
// get undo and redo buttons // click the buttons
await page.locator('.buttonicon-undo').click() // removes foo
await page.locator('.buttonicon-redo').click() // resends foo
await expect($firstTextElement).toHaveText(newString);
const finalValue = await padBody.locator('div').first().textContent();
expect(finalValue).toBe(modifiedValue); // expect the value to change
});
test('redo some typing with keypress', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element inside the editable space
const $firstTextElement = padBody.locator('div span').first();
const originalValue = await $firstTextElement.textContent(); // get the original value
const newString = 'Foo';
await padBody.click()
await clearPadContent(page)
await writeToPad(page, newString); // send line 1 to the pad
const modifiedValue = await $firstTextElement.textContent(); // get the modified value
expect(modifiedValue).not.toBe(originalValue); // expect the value to change
// undo the change
await padBody.click()
await page.keyboard.press('Control+Z');
await page.keyboard.press('Control+Y'); // redo the change
await expect($firstTextElement).toHaveText(newString);
const finalValue = await padBody.locator('div').first().textContent();
expect(finalValue).toBe(modifiedValue); // expect the value to change
});
});

View file

@ -0,0 +1,30 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('strikethrough button', function () {
test('makes text strikethrough', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await $firstTextElement.selectText()
// get the strikethrough button and click it
await page.locator('.buttonicon-strikethrough').click();
// ace creates a new dom element when you press a button, just get the first text element again
// is there a <i> element now?
await expect($firstTextElement.locator('s')).toHaveCount(1);
// make sure the text hasn't changed
expect(await $firstTextElement.textContent()).toEqual(await $firstTextElement.textContent());
});
});

View file

@ -0,0 +1,37 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
// create a new pad before each test run
await goToNewPad(page);
})
// deactivated, we need a nice way to get the timeslider, this is ugly
test.describe('timeslider button takes you to the timeslider of a pad', function () {
test('timeslider contained in URL', async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
await writeToPad(page, 'Foo'); // send line 1 to the pad
// get the first text element inside the editable space
const $firstTextElement = padBody.locator('div span').first();
const originalValue = await $firstTextElement.textContent(); // get the original value
await $firstTextElement.click()
await writeToPad(page, 'Testing'); // send line 1 to the pad
const modifiedValue = await $firstTextElement.textContent(); // get the modified value
expect(modifiedValue).not.toBe(originalValue); // expect the value to change
const $timesliderButton = page.locator('.buttonicon-history');
await $timesliderButton.click(); // So click the timeslider link
await page.waitForSelector('#timeslider-wrapper')
const iFrameURL = page.url(); // get the url
const inTimeslider = iFrameURL.indexOf('timeslider') !== -1;
expect(inTimeslider).toBe(true); // expect the value to change
});
});

View file

@ -0,0 +1,76 @@
'use strict';
import {expect, Page, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
import {gotoTimeslider} from "../helper/timeslider";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('timeslider follow', function () {
// TODO needs test if content is also followed, when user a makes edits
// while user b is in the timeslider
test("content as it's added to timeslider", async function ({page}) {
// send 6 revisions
const revs = 6;
const message = 'a\n\n\n\n\n\n\n\n\n\n';
const newLines = message.split('\n').length;
for (let i = 0; i < revs; i++) {
await writeToPad(page, message)
}
await gotoTimeslider(page,0);
expect(page.url()).toContain('#0');
const originalTop = await page.evaluate(() => {
return window.document.querySelector('#innerdocbody')!.getBoundingClientRect().top;
});
// set to follow contents as it arrives
await page.check('#options-followContents');
await page.click('#playpause_button_icon');
// wait for the scroll
await page.waitForTimeout(1000)
const currentOffset = await page.evaluate(() => {
return window.document.querySelector('#innerdocbody')!.getBoundingClientRect().top;
});
expect(currentOffset).toBeLessThanOrEqual(originalTop);
});
/**
* Tests for bug described in #4389
* The goal is to scroll to the first line that contains a change right before
* the change is applied.
*/
test('only to lines that exist in the pad view, regression test for #4389', async function ({page}) {
const padBody = await getPadBody(page)
await padBody.click()
await clearPadContent(page)
await writeToPad(page,'Test line\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' +
'\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
await padBody.locator('div').nth(40).click();
await writeToPad(page, 'Another test line');
await gotoTimeslider(page, 200);
// set to follow contents as it arrives
await page.check('#options-followContents');
await page.waitForTimeout(1000)
const oldYPosition = await page.locator('#editorcontainerbox').evaluate((el) => {
return el.scrollTop;
})
expect(oldYPosition).toBe(0);
});
});

View file

@ -0,0 +1,56 @@
'use strict';
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('undo button', function () {
test('undo some typing by clicking undo button', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
// get the first text element inside the editable space
const firstTextElement = padBody.locator('div').first()
const originalValue = await firstTextElement.textContent(); // get the original value
await firstTextElement.focus()
await writeToPad(page, 'foo'); // send line 1 to the pad
const modifiedValue = await firstTextElement.textContent(); // get the modified value
expect(modifiedValue).not.toBe(originalValue); // expect the value to change
// get clear authorship button as a variable
const undoButton = page.locator('.buttonicon-undo')
await undoButton.click() // click the button
await expect(firstTextElement).toHaveText(originalValue!);
});
test('undo some typing using a keypress', async function ({page}) {
const padBody = await getPadBody(page);
await padBody.click()
await clearPadContent(page)
// get the first text element inside the editable space
const firstTextElement = padBody.locator('div').first()
const originalValue = await firstTextElement.textContent(); // get the original value
await firstTextElement.focus()
await writeToPad(page, 'foo'); // send line 1 to the pad
const modifiedValue = await firstTextElement.textContent(); // get the modified value
expect(modifiedValue).not.toBe(originalValue); // expect the value to change
// undo the change
await page.keyboard.press('Control+Z');
await page.waitForTimeout(1000)
await expect(firstTextElement).toHaveText(originalValue!);
});
});

View file

@ -0,0 +1,127 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
// create a new pad before each test run
await goToNewPad(page);
})
test.describe('unordered_list.js', function () {
test.describe('assign unordered list', function () {
test('insert unordered list text then removes by outdent', async function ({page}) {
const padBody = await getPadBody(page);
const originalText = await padBody.locator('div').first().textContent();
const $insertunorderedlistButton = page.locator('.buttonicon-insertunorderedlist');
await $insertunorderedlistButton.click();
await expect(padBody.locator('div').first()).toHaveText(originalText!);
await expect(padBody.locator('div ul li')).toHaveCount(1);
// remove indentation by bullet and ensure text string remains the same
const $outdentButton = page.locator('.buttonicon-outdent');
await $outdentButton.click();
await expect(padBody.locator('div').first()).toHaveText(originalText!);
});
});
test.describe('unassign unordered list', function () {
// create a new pad before each test run
test('insert unordered list text then remove by clicking list again', async function ({page}) {
const padBody = await getPadBody(page);
const originalText = await padBody.locator('div').first().textContent();
await padBody.locator('div').first().selectText()
const $insertunorderedlistButton = page.locator('.buttonicon-insertunorderedlist');
await $insertunorderedlistButton.click();
await expect(padBody.locator('div').first()).toHaveText(originalText!);
await expect(padBody.locator('div ul li')).toHaveCount(1);
// remove indentation by bullet and ensure text string remains the same
await $insertunorderedlistButton.click();
await expect(padBody.locator('div').locator('ul')).toHaveCount(0)
});
});
test.describe('keep unordered list on enter key', function () {
test('Keeps the unordered list on enter for the new line', async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
await expect(padBody.locator('div')).toHaveCount(1)
const $insertorderedlistButton = page.locator('.buttonicon-insertunorderedlist')
await $insertorderedlistButton.click();
// type a bit, make a line break and type again
const $firstTextElement = padBody.locator('div').first();
await $firstTextElement.click()
await page.keyboard.type('line 1');
await page.keyboard.press('Enter');
await page.keyboard.type('line 2');
await page.keyboard.press('Enter');
await expect(padBody.locator('div span')).toHaveCount(2);
const $newSecondLine = padBody.locator('div').nth(1)
await expect($newSecondLine.locator('ul')).toHaveCount(1);
await expect($newSecondLine).toHaveText('line 2');
});
});
test.describe('Pressing Tab in an UL increases and decreases indentation', function () {
test('indent and de-indent list item with keypress', async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await $firstTextElement.selectText();
const $insertunorderedlistButton = page.locator('.buttonicon-insertunorderedlist');
await $insertunorderedlistButton.click();
await padBody.locator('div').first().click();
await page.keyboard.press('Home');
await page.keyboard.press('Tab');
await expect(padBody.locator('div').first().locator('.list-bullet2')).toHaveCount(1);
await page.keyboard.press('Shift+Tab');
await expect(padBody.locator('div').first().locator('.list-bullet1')).toHaveCount(1);
});
});
test.describe('Pressing indent/outdent button in an UL increases and decreases indentation ' +
'and bullet / ol formatting', function () {
test('indent and de-indent list item with indent button', async function ({page}) {
const padBody = await getPadBody(page);
// get the first text element out of the inner iframe
const $firstTextElement = padBody.locator('div').first();
// select this text element
await $firstTextElement.selectText();
const $insertunorderedlistButton = page.locator('.buttonicon-insertunorderedlist');
await $insertunorderedlistButton.click();
await page.locator('.buttonicon-indent').click();
await expect(padBody.locator('div').first().locator('.list-bullet2')).toHaveCount(1);
const outdentButton = page.locator('.buttonicon-outdent');
await outdentButton.click();
await expect(padBody.locator('div').first().locator('.list-bullet1')).toHaveCount(1);
});
});
});

View file

@ -0,0 +1,51 @@
import {expect, test} from "@playwright/test";
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
test.beforeEach(async ({ page })=>{
await goToNewPad(page);
})
test.describe('entering a URL makes a link', function () {
for (const url of ['https://etherpad.org', 'www.etherpad.org', 'https://www.etherpad.org']) {
test(url, async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
const url = 'https://etherpad.org';
await writeToPad(page, url);
await expect(padBody.locator('div').first()).toHaveText(url);
await expect(padBody.locator('a')).toHaveText(url);
await expect(padBody.locator('a')).toHaveAttribute('href', url);
});
}
});
test.describe('special characters inside URL', async function () {
for (const char of '-:@_.,~%+/?=&#!;()[]$\'*') {
const url = `https://etherpad.org/${char}foo`;
test(url, async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
await padBody.click()
await clearPadContent(page)
await writeToPad(page, url);
await expect(padBody.locator('div').first()).toHaveText(url);
await expect(padBody.locator('a')).toHaveText(url);
await expect(padBody.locator('a')).toHaveAttribute('href', url);
});
}
});
test.describe('punctuation after URL is ignored', ()=> {
for (const char of ':.,;?!)]\'*') {
const want = 'https://etherpad.org';
const input = want + char;
test(input, async function ({page}) {
const padBody = await getPadBody(page);
await clearPadContent(page)
await writeToPad(page, input);
await expect(padBody.locator('a')).toHaveCount(1);
await expect(padBody.locator('a')).toHaveAttribute('href', want);
});
}
});