mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-25 09:56:15 -04:00
Added playwright tests. (#6212)
* Added playwright tests. * Added clear authorship color. * Ported enter ts. * Ported more tests. * Commented helper tests. * Fixed admin tests. * Fixed. * Fixed admin pages not there. * Fixed waiting. * Upload playwright report. * Remove saucelabs * Fixed waiting. * Fixed upload artifact. * Also install deps. * Added retry mechanism. * Added timeout for restart etherpad server. * Fixed tests. * Added frontend playwright tests.
This commit is contained in:
parent
db46ffb63b
commit
c2699e4528
40 changed files with 1568 additions and 1285 deletions
27
src/tests/frontend-new/specs/alphabet.spec.ts
Normal file
27
src/tests/frontend-new/specs/alphabet.spec.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import {expect, Page, test} from "@playwright/test";
|
||||
import {clearPadContent, getPadBody, getPadOuter, goToNewPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('All the alphabet works n stuff', () => {
|
||||
const expectedString = 'abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
test('when you enter any char it appears right', async ({page}) => {
|
||||
|
||||
// get the inner iframe
|
||||
const innerFrame = await getPadBody(page!);
|
||||
|
||||
await innerFrame.click();
|
||||
|
||||
// delete possible old content
|
||||
await clearPadContent(page!);
|
||||
|
||||
|
||||
await page.keyboard.type(expectedString);
|
||||
const text = await innerFrame.locator('div').innerText();
|
||||
expect(text).toBe(expectedString);
|
||||
});
|
||||
});
|
50
src/tests/frontend-new/specs/bold.spec.ts
Normal file
50
src/tests/frontend-new/specs/bold.spec.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {randomInt} from "node:crypto";
|
||||
import {getPadBody, goToNewPad, selectAllText} from "../helper/padHelper";
|
||||
import exp from "node:constants";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('bold button', ()=>{
|
||||
|
||||
test('makes text bold on click', async ({page}) => {
|
||||
// get the inner iframe
|
||||
const innerFrame = await getPadBody(page);
|
||||
|
||||
await innerFrame.click()
|
||||
// Select pad text
|
||||
await selectAllText(page);
|
||||
await page.keyboard.type("Hi Etherpad");
|
||||
await selectAllText(page);
|
||||
|
||||
// click the bold button
|
||||
await page.locator("button[data-l10n-id='pad.toolbar.bold.title']").click();
|
||||
|
||||
|
||||
// check if the text is bold
|
||||
expect(await innerFrame.locator('b').innerText()).toBe('Hi Etherpad');
|
||||
})
|
||||
|
||||
test('makes text bold on keypress', async ({page}) => {
|
||||
// get the inner iframe
|
||||
const innerFrame = await getPadBody(page);
|
||||
|
||||
await innerFrame.click()
|
||||
// Select pad text
|
||||
await selectAllText(page);
|
||||
await page.keyboard.type("Hi Etherpad");
|
||||
await selectAllText(page);
|
||||
|
||||
// Press CTRL + B
|
||||
await page.keyboard.down('Control');
|
||||
await page.keyboard.press('b');
|
||||
await page.keyboard.up('Control');
|
||||
|
||||
|
||||
// check if the text is bold
|
||||
expect(await innerFrame.locator('b').innerText()).toBe('Hi Etherpad');
|
||||
})
|
||||
|
||||
})
|
35
src/tests/frontend-new/specs/change_user_name.spec.ts
Normal file
35
src/tests/frontend-new/specs/change_user_name.spec.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {randomInt} from "node:crypto";
|
||||
import {goToNewPad, sendChatMessage, setUserName, showChat, toggleUserList} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
|
||||
test("Remembers the username after a refresh", async ({page}) => {
|
||||
await toggleUserList(page);
|
||||
await setUserName(page,'😃')
|
||||
await toggleUserList(page)
|
||||
|
||||
await page.reload();
|
||||
await toggleUserList(page);
|
||||
const usernameField = page.locator("input[data-l10n-id='pad.userlist.entername']");
|
||||
await expect(usernameField).toHaveValue('😃');
|
||||
})
|
||||
|
||||
|
||||
test('Own user name is shown when you enter a chat', async ({page})=> {
|
||||
const chatMessage = 'O hi';
|
||||
|
||||
await toggleUserList(page);
|
||||
await setUserName(page,'😃');
|
||||
await toggleUserList(page);
|
||||
|
||||
await showChat(page);
|
||||
await sendChatMessage(page,chatMessage);
|
||||
const chatText = await page.locator('#chattext').locator('p').innerText();
|
||||
expect(chatText).toContain('😃')
|
||||
expect(chatText).toContain(chatMessage)
|
||||
});
|
116
src/tests/frontend-new/specs/chat.spec.ts
Normal file
116
src/tests/frontend-new/specs/chat.spec.ts
Normal file
|
@ -0,0 +1,116 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {randomInt} from "node:crypto";
|
||||
import {
|
||||
appendQueryParams,
|
||||
disableStickyChatviaIcon,
|
||||
enableStickyChatviaIcon,
|
||||
getChatMessage,
|
||||
getChatTime,
|
||||
getChatUserName,
|
||||
getCurrentChatMessageCount, goToNewPad, hideChat, isChatBoxShown, isChatBoxSticky,
|
||||
sendChatMessage,
|
||||
showChat,
|
||||
} from "../helper/padHelper";
|
||||
import {disableStickyChat, enableStickyChatviaSettings, hideSettings, showSettings} from "../helper/settingsHelper";
|
||||
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
|
||||
test('opens chat, sends a message, makes sure it exists on the page and hides chat', async ({page}) => {
|
||||
const chatValue = "JohnMcLear"
|
||||
|
||||
// Open chat
|
||||
await showChat(page);
|
||||
await sendChatMessage(page, chatValue);
|
||||
|
||||
expect(await getCurrentChatMessageCount(page)).toBe(1);
|
||||
const username = await getChatUserName(page)
|
||||
const time = await getChatTime(page)
|
||||
const chatMessage = await getChatMessage(page)
|
||||
|
||||
expect(username).toBe('unnamed:');
|
||||
const regex = new RegExp('^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$');
|
||||
expect(time).toMatch(regex);
|
||||
expect(chatMessage).toBe(" "+chatValue);
|
||||
})
|
||||
|
||||
test("makes sure that an empty message can't be sent", async function ({page}) {
|
||||
const chatValue = 'mluto';
|
||||
|
||||
await showChat(page);
|
||||
|
||||
await sendChatMessage(page,"");
|
||||
// Send a message
|
||||
await sendChatMessage(page,chatValue);
|
||||
|
||||
expect(await getCurrentChatMessageCount(page)).toBe(1);
|
||||
|
||||
// check that the received message is not the empty one
|
||||
const username = await getChatUserName(page)
|
||||
const time = await getChatTime(page);
|
||||
const chatMessage = await getChatMessage(page);
|
||||
|
||||
expect(username).toBe('unnamed:');
|
||||
const regex = new RegExp('^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$');
|
||||
expect(time).toMatch(regex);
|
||||
expect(chatMessage).toBe(" "+chatValue);
|
||||
});
|
||||
|
||||
test('makes chat stick to right side of the screen via settings, remove sticky via settings, close it', async ({page}) =>{
|
||||
await showSettings(page);
|
||||
|
||||
await enableStickyChatviaSettings(page);
|
||||
expect(await isChatBoxShown(page)).toBe(true);
|
||||
expect(await isChatBoxSticky(page)).toBe(true);
|
||||
|
||||
await disableStickyChat(page);
|
||||
expect(await isChatBoxShown(page)).toBe(true);
|
||||
expect(await isChatBoxSticky(page)).toBe(false);
|
||||
await hideSettings(page);
|
||||
await hideChat(page);
|
||||
expect(await isChatBoxShown(page)).toBe(false);
|
||||
expect(await isChatBoxSticky(page)).toBe(false);
|
||||
});
|
||||
|
||||
test('makes chat stick to right side of the screen via icon on the top right, ' +
|
||||
'remove sticky via icon, close it', async function ({page}) {
|
||||
await showChat(page);
|
||||
|
||||
await enableStickyChatviaIcon(page);
|
||||
expect(await isChatBoxShown(page)).toBe(true);
|
||||
expect(await isChatBoxSticky(page)).toBe(true);
|
||||
|
||||
await disableStickyChatviaIcon(page);
|
||||
expect(await isChatBoxShown(page)).toBe(true);
|
||||
expect(await isChatBoxSticky(page)).toBe(false);
|
||||
|
||||
await hideChat(page);
|
||||
expect(await isChatBoxSticky(page)).toBe(false);
|
||||
expect(await isChatBoxShown(page)).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
test('Checks showChat=false URL Parameter hides chat then' +
|
||||
' when removed it shows chat', async function ({page}) {
|
||||
|
||||
// get a new pad, but don't clear the cookies
|
||||
await appendQueryParams(page, {
|
||||
showChat: 'false'
|
||||
});
|
||||
|
||||
const chaticon = page.locator('#chaticon')
|
||||
|
||||
|
||||
// chat should be hidden.
|
||||
expect(await chaticon.isVisible()).toBe(false);
|
||||
|
||||
// get a new pad, but don't clear the cookies
|
||||
await goToNewPad(page);
|
||||
const secondChatIcon = page.locator('#chaticon')
|
||||
|
||||
// chat should be visible.
|
||||
expect(await secondChatIcon.isVisible()).toBe(true)
|
||||
});
|
87
src/tests/frontend-new/specs/clear_authorship_color.spec.ts
Normal file
87
src/tests/frontend-new/specs/clear_authorship_color.spec.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {
|
||||
clearAuthorship,
|
||||
clearPadContent,
|
||||
getPadBody,
|
||||
goToNewPad, pressUndoButton,
|
||||
selectAllText,
|
||||
undoChanges,
|
||||
writeToPad
|
||||
} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test('clear authorship color', async ({page}) => {
|
||||
// get the inner iframe
|
||||
const innerFrame = await getPadBody(page);
|
||||
const padText = "Hello"
|
||||
|
||||
// type some text
|
||||
await clearPadContent(page);
|
||||
await writeToPad(page, padText);
|
||||
const retrievedClasses = await innerFrame.locator('div span').nth(0).getAttribute('class')
|
||||
expect(retrievedClasses).toContain('author');
|
||||
|
||||
// select the text
|
||||
await innerFrame.click()
|
||||
await selectAllText(page);
|
||||
|
||||
await clearAuthorship(page);
|
||||
// does the first div include an author class?
|
||||
const firstDivClass = await innerFrame.locator('div').nth(0).getAttribute('class');
|
||||
expect(firstDivClass).not.toContain('author');
|
||||
const classes = page.locator('div.disconnected')
|
||||
expect(await classes.isVisible()).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
test("makes text clear authorship colors and checks it can't be undone", async function ({page}) {
|
||||
const innnerPad = await getPadBody(page);
|
||||
const padText = "Hello"
|
||||
|
||||
// type some text
|
||||
await clearPadContent(page);
|
||||
await writeToPad(page, padText);
|
||||
|
||||
// get the first text element out of the inner iframe
|
||||
const firstDivClass = innnerPad.locator('div').nth(0)
|
||||
const retrievedClasses = await innnerPad.locator('div span').nth(0).getAttribute('class')
|
||||
expect(retrievedClasses).toContain('author');
|
||||
|
||||
|
||||
await firstDivClass.focus()
|
||||
await clearAuthorship(page);
|
||||
expect(await firstDivClass.getAttribute('class')).not.toContain('author');
|
||||
|
||||
await undoChanges(page);
|
||||
const changedFirstDiv = innnerPad.locator('div').nth(0)
|
||||
expect(await changedFirstDiv.getAttribute('class')).not.toContain('author');
|
||||
|
||||
|
||||
await pressUndoButton(page);
|
||||
const secondChangedFirstDiv = innnerPad.locator('div').nth(0)
|
||||
expect(await secondChangedFirstDiv.getAttribute('class')).not.toContain('author');
|
||||
});
|
||||
|
||||
|
||||
// Test for https://github.com/ether/etherpad-lite/issues/5128
|
||||
test('clears authorship when first line has line attributes', async function ({page}) {
|
||||
// Make sure there is text with author info. The first line must have a line attribute.
|
||||
const padBody = await getPadBody(page);
|
||||
await padBody.click()
|
||||
await clearPadContent(page);
|
||||
await writeToPad(page,'Hello')
|
||||
await page.locator('.buttonicon-insertunorderedlist').click();
|
||||
const retrievedClasses = await padBody.locator('div span').nth(0).getAttribute('class')
|
||||
expect(retrievedClasses).toContain('author');
|
||||
await padBody.click()
|
||||
await selectAllText(page);
|
||||
await clearAuthorship(page);
|
||||
const retrievedClasses2 = await padBody.locator('div span').nth(0).getAttribute('class')
|
||||
expect(retrievedClasses2).not.toContain('author');
|
||||
|
||||
expect(await page.locator('[class*="author-"]').count()).toBe(0)
|
||||
});
|
94
src/tests/frontend-new/specs/collab_client.spec.ts
Normal file
94
src/tests/frontend-new/specs/collab_client.spec.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import {clearPadContent, getPadBody, goToNewPad, goToPad, writeToPad} from "../helper/padHelper";
|
||||
import {expect, Page, test} from "@playwright/test";
|
||||
|
||||
let padId = "";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
padId = await goToNewPad(page);
|
||||
const body = await getPadBody(page);
|
||||
await body.click();
|
||||
await clearPadContent(page);
|
||||
await writeToPad(page, "Hello World");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "Hello World");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "Hello World");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "Hello World");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "Hello World");
|
||||
await page.keyboard.press('Enter');
|
||||
})
|
||||
|
||||
test.describe('Messages in the COLLABROOM', function () {
|
||||
const user1Text = 'text created by user 1';
|
||||
const user2Text = 'text created by user 2';
|
||||
|
||||
const replaceLineText = async (lineNumber: number, newText: string, page: Page) => {
|
||||
const body = await getPadBody(page)
|
||||
|
||||
const div = body.locator('div').nth(lineNumber)
|
||||
|
||||
// simulate key presses to delete content
|
||||
await div.locator('span').selectText() // select all
|
||||
await page.keyboard.press('Backspace') // clear the first line
|
||||
await page.keyboard.type(newText) // insert the string
|
||||
};
|
||||
|
||||
test('bug #4978 regression test', async function ({browser}) {
|
||||
// The bug was triggered by receiving a change from another user while simultaneously composing
|
||||
// a character and waiting for an acknowledgement of a previously sent change.
|
||||
|
||||
// User 1
|
||||
const context1 = await browser.newContext();
|
||||
const page1 = await context1.newPage();
|
||||
await goToPad(page1, padId)
|
||||
const body1 = await getPadBody(page1)
|
||||
// Perform actions as User 1...
|
||||
|
||||
// User 2
|
||||
const context2 = await browser.newContext();
|
||||
const page2 = await context2.newPage();
|
||||
await goToPad(page2, padId)
|
||||
const body2 = await getPadBody(page1)
|
||||
|
||||
await replaceLineText(0, user1Text,page1);
|
||||
|
||||
const text = await body2.locator('div').nth(0).textContent()
|
||||
const res = text === user1Text
|
||||
expect(res).toBe(true)
|
||||
|
||||
// User 1 starts a character composition.
|
||||
|
||||
|
||||
await replaceLineText(1, user2Text, page2)
|
||||
|
||||
await expect(body1.locator('div').nth(1)).toHaveText(user2Text)
|
||||
|
||||
|
||||
// Users 1 and 2 make some more changes.
|
||||
await replaceLineText(3, user2Text, page2);
|
||||
|
||||
await expect(body1.locator('div').nth(3)).toHaveText(user2Text)
|
||||
|
||||
await replaceLineText(2, user1Text, page1);
|
||||
await expect(body2.locator('div').nth(2)).toHaveText(user1Text)
|
||||
|
||||
// All changes should appear in both views.
|
||||
const expectedLines = [
|
||||
user1Text,
|
||||
user2Text,
|
||||
user1Text,
|
||||
user2Text,
|
||||
];
|
||||
|
||||
for (let i=0;i<expectedLines.length;i++){
|
||||
expect(await body1.locator('div').nth(i).textContent()).toBe(expectedLines[i]);
|
||||
}
|
||||
|
||||
for (let i=0;i<expectedLines.length;i++){
|
||||
expect(await body2.locator('div').nth(i).textContent()).toBe(expectedLines[i]);
|
||||
}
|
||||
});
|
||||
});
|
22
src/tests/frontend-new/specs/delete.spec.ts
Normal file
22
src/tests/frontend-new/specs/delete.spec.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {clearPadContent, getPadBody, goToNewPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
|
||||
test('delete keystroke', async ({page}) => {
|
||||
const padText = "Hello World this is a test"
|
||||
const body = await getPadBody(page)
|
||||
await body.click()
|
||||
await clearPadContent(page)
|
||||
await page.keyboard.type(padText)
|
||||
// Navigate to the end of the text
|
||||
await page.keyboard.press('End');
|
||||
// Delete the last character
|
||||
await page.keyboard.press('Backspace');
|
||||
const text = await body.locator('div').innerText();
|
||||
expect(text).toBe(padText.slice(0, -1));
|
||||
})
|
146
src/tests/frontend-new/specs/embed_value.spec.ts
Normal file
146
src/tests/frontend-new/specs/embed_value.spec.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import {expect, Page, test} from "@playwright/test";
|
||||
import {goToNewPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('embed links', function () {
|
||||
const objectify = function (str: string) {
|
||||
const hash = {};
|
||||
const parts = str.split('&');
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const keyValue = parts[i].split('=');
|
||||
// @ts-ignore
|
||||
hash[keyValue[0]] = keyValue[1];
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
const checkiFrameCode = async function (embedCode: string, readonly: boolean, page: Page) {
|
||||
// turn the code into an html element
|
||||
|
||||
await page.setContent(embedCode, {waitUntil: 'load'})
|
||||
const locator = page.locator('body').locator('iframe').last()
|
||||
|
||||
|
||||
// read and check the frame attributes
|
||||
const width = await locator.getAttribute('width');
|
||||
const height = await locator.getAttribute('height');
|
||||
const name = await locator.getAttribute('name');
|
||||
expect(width).toBe('100%');
|
||||
expect(height).toBe('600');
|
||||
expect(name).toBe(readonly ? 'embed_readonly' : 'embed_readwrite');
|
||||
|
||||
// parse the url
|
||||
const src = (await locator.getAttribute('src'))!;
|
||||
const questionMark = src.indexOf('?');
|
||||
const url = src.substring(0, questionMark);
|
||||
const paramsStr = src.substring(questionMark + 1);
|
||||
const params = objectify(paramsStr);
|
||||
|
||||
const expectedParams = {
|
||||
showControls: 'true',
|
||||
showChat: 'true',
|
||||
showLineNumbers: 'true',
|
||||
useMonospaceFont: 'false',
|
||||
};
|
||||
|
||||
// check the url
|
||||
if (readonly) {
|
||||
expect(url.indexOf('r.') > 0).toBe(true);
|
||||
} else {
|
||||
expect(url).toBe(await page.evaluate(() => window.location.href));
|
||||
}
|
||||
|
||||
// check if all parts of the url are like expected
|
||||
expect(params).toEqual(expectedParams);
|
||||
};
|
||||
|
||||
test.describe('read and write', function () {
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// 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}) {
|
||||
|
||||
const shareButton = page.locator('.buttonicon-embed')
|
||||
// open share dropdown
|
||||
await shareButton.click()
|
||||
|
||||
// get the link of the share field + the actual pad url and compare them
|
||||
const shareLink = await page.locator('#linkinput').inputValue()
|
||||
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}) {
|
||||
|
||||
const shareButton = page.locator('.buttonicon-embed')
|
||||
await shareButton.click()
|
||||
|
||||
// get the link of the share field + the actual pad url and compare them
|
||||
const embedCode = await page.locator('#embedinput').inputValue()
|
||||
|
||||
|
||||
await checkiFrameCode(embedCode, false, page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('when read only option is set', function () {
|
||||
test.beforeEach(async ({ page })=>{
|
||||
// create a new pad before each test run
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('the share link', function () {
|
||||
test('shows a read only url', async function ({page}) {
|
||||
|
||||
// open share dropdown
|
||||
const shareButton = page.locator('.buttonicon-embed')
|
||||
await shareButton.click()
|
||||
const readonlyCheckbox = page.locator('#readonlyinput')
|
||||
await readonlyCheckbox.click({
|
||||
force: true
|
||||
})
|
||||
await page.waitForSelector('#readonlyinput:checked')
|
||||
|
||||
// get the link of the share field + the actual pad url and compare them
|
||||
const shareLink = await page.locator('#linkinput').inputValue()
|
||||
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}) {
|
||||
|
||||
|
||||
// open share dropdown
|
||||
const shareButton = page.locator('.buttonicon-embed')
|
||||
await shareButton.click()
|
||||
|
||||
// check read only checkbox, a bit hacky
|
||||
const readonlyCheckbox = page.locator('#readonlyinput')
|
||||
await readonlyCheckbox.click({
|
||||
force: true
|
||||
})
|
||||
|
||||
await page.waitForSelector('#readonlyinput:checked')
|
||||
|
||||
|
||||
// get the link of the share field + the actual pad url and compare them
|
||||
const embedCode = await page.locator('#embedinput').inputValue()
|
||||
|
||||
await checkiFrameCode(embedCode, true, page);
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
})
|
63
src/tests/frontend-new/specs/enter.spec.ts
Normal file
63
src/tests/frontend-new/specs/enter.spec.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
import {expect, test} from "@playwright/test";
|
||||
import {getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('enter keystroke', function () {
|
||||
|
||||
test('creates a new line & puts cursor onto a new line', async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
|
||||
// get the first text element out of the inner iframe
|
||||
const firstTextElement = padBody.locator('div').nth(0)
|
||||
|
||||
// get the original string value minus the last char
|
||||
const originalTextValue = await firstTextElement.textContent();
|
||||
|
||||
// simulate key presses to enter content
|
||||
await firstTextElement.click()
|
||||
await page.keyboard.press('Home');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
const updatedFirstElement = padBody.locator('div').nth(0)
|
||||
expect(await updatedFirstElement.textContent()).toBe('')
|
||||
|
||||
const newSecondLine = padBody.locator('div').nth(1);
|
||||
// expect the second line to be the same as the original first line.
|
||||
expect(await newSecondLine.textContent()).toBe(originalTextValue);
|
||||
});
|
||||
|
||||
test('enter is always visible after event', async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
const originalLength = await padBody.locator('div').count();
|
||||
let lastLine = padBody.locator('div').last();
|
||||
|
||||
// simulate key presses to enter content
|
||||
let i = 0;
|
||||
const numberOfLines = 15;
|
||||
while (i < numberOfLines) {
|
||||
lastLine = padBody.locator('div').last();
|
||||
await lastLine.focus();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
// check we can see the caret..
|
||||
i++;
|
||||
}
|
||||
|
||||
expect(await padBody.locator('div').count()).toBe(numberOfLines + originalLength);
|
||||
|
||||
// is edited line fully visible?
|
||||
const lastDiv = padBody.locator('div').last()
|
||||
const lastDivOffset = await lastDiv.boundingBox();
|
||||
const bottomOfLastLine = lastDivOffset!.y + lastDivOffset!.height;
|
||||
const scrolledWindow = page.frames()[0];
|
||||
const windowOffset = await scrolledWindow.evaluate(() => window.pageYOffset);
|
||||
const windowHeight = await scrolledWindow.evaluate(() => window.innerHeight);
|
||||
|
||||
expect(windowOffset + windowHeight).toBeGreaterThan(bottomOfLastLine);
|
||||
});
|
||||
});
|
241
src/tests/frontend-new/specs/indentation.spec.ts
Normal file
241
src/tests/frontend-new/specs/indentation.spec.ts
Normal file
|
@ -0,0 +1,241 @@
|
|||
import {expect, test} from "@playwright/test";
|
||||
import {clearPadContent, getPadBody, goToNewPad, writeToPad} from "../helper/padHelper";
|
||||
|
||||
test.beforeEach(async ({ page })=>{
|
||||
await goToNewPad(page);
|
||||
})
|
||||
|
||||
test.describe('indentation button', function () {
|
||||
test('indent text 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()
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
|
||||
const uls = padBody.locator('div').first().locator('ul li')
|
||||
await expect(uls).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('indent text with button', async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
const uls = padBody.locator('div').first().locator('ul li')
|
||||
await expect(uls).toHaveCount(1);
|
||||
});
|
||||
|
||||
|
||||
test('keeps the indent on enter for the new line', async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
await padBody.click()
|
||||
await clearPadContent(page)
|
||||
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
// type a bit, make a line break and type again
|
||||
await padBody.locator('div').first().focus()
|
||||
await page.keyboard.type('line 1')
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.type('line 2')
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
const $newSecondLine = padBody.locator('div span').nth(1)
|
||||
|
||||
const hasULElement = padBody.locator('ul li')
|
||||
|
||||
await expect(hasULElement).toHaveCount(3);
|
||||
await expect($newSecondLine).toHaveText('line 2');
|
||||
});
|
||||
|
||||
|
||||
test('indents text with spaces on enter if previous line ends ' +
|
||||
"with ':', '[', '(', or '{'", async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
await padBody.click()
|
||||
await clearPadContent(page)
|
||||
// type a bit, make a line break and type again
|
||||
const $firstTextElement = padBody.locator('div').first();
|
||||
await writeToPad(page, "line with ':'");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "line with '['");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "line with '('");
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "line with '{{}'");
|
||||
|
||||
await expect(padBody.locator('div').nth(3)).toHaveText("line with '{{}'");
|
||||
|
||||
// we validate bottom to top for easier implementation
|
||||
|
||||
|
||||
// curly braces
|
||||
const $lineWithCurlyBraces = padBody.locator('div').nth(3)
|
||||
await $lineWithCurlyBraces.click();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.type('{{');
|
||||
|
||||
// cannot use sendkeys('{enter}') here, browser does not read the command properly
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
expect(await padBody.locator('div').nth(4).textContent()).toMatch(/\s{4}/); // tab === 4 spaces
|
||||
|
||||
|
||||
|
||||
// parenthesis
|
||||
const $lineWithParenthesis = padBody.locator('div').nth(2)
|
||||
await $lineWithParenthesis.click();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.type('(');
|
||||
await page.keyboard.press('Enter');
|
||||
const $lineAfterParenthesis = padBody.locator('div').nth(3)
|
||||
expect(await $lineAfterParenthesis.textContent()).toMatch(/\s{4}/);
|
||||
|
||||
// bracket
|
||||
const $lineWithBracket = padBody.locator('div').nth(1)
|
||||
await $lineWithBracket.click();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.type('[');
|
||||
await page.keyboard.press('Enter');
|
||||
const $lineAfterBracket = padBody.locator('div').nth(2);
|
||||
expect(await $lineAfterBracket.textContent()).toMatch(/\s{4}/);
|
||||
|
||||
// colon
|
||||
const $lineWithColon = padBody.locator('div').first();
|
||||
await $lineWithColon.click();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.type(':');
|
||||
await page.keyboard.press('Enter');
|
||||
const $lineAfterColon = padBody.locator('div').nth(1);
|
||||
expect(await $lineAfterColon.textContent()).toMatch(/\s{4}/);
|
||||
});
|
||||
|
||||
test('appends indentation to the indent of previous line if previous line ends ' +
|
||||
"with ':', '[', '(', or '{'", async function ({page}) {
|
||||
const padBody = await getPadBody(page);
|
||||
await padBody.click()
|
||||
await clearPadContent(page)
|
||||
|
||||
// type a bit, make a line break and type again
|
||||
await writeToPad(page, " line with some indentation and ':'")
|
||||
await page.keyboard.press('Enter');
|
||||
await writeToPad(page, "line 2")
|
||||
|
||||
const $lineWithColon = padBody.locator('div').first();
|
||||
await $lineWithColon.click();
|
||||
await page.keyboard.press('End');
|
||||
await page.keyboard.type(':');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
const $lineAfterColon = padBody.locator('div').nth(1);
|
||||
// previous line indentation + regular tab (4 spaces)
|
||||
expect(await $lineAfterColon.textContent()).toMatch(/\s{6}/);
|
||||
});
|
||||
|
||||
test("issue #2772 shows '*' when multiple indented lines " +
|
||||
' receive a style and are outdented', async function ({page}) {
|
||||
|
||||
const padBody = await getPadBody(page);
|
||||
await padBody.click()
|
||||
await clearPadContent(page)
|
||||
|
||||
const inner = padBody.locator('div').first();
|
||||
// make sure pad has more than one line
|
||||
await inner.click()
|
||||
await page.keyboard.type('First');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.type('Second');
|
||||
|
||||
|
||||
// indent first 2 lines
|
||||
await padBody.locator('div').nth(0).selectText();
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
await padBody.locator('div').nth(1).selectText();
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
|
||||
await expect(padBody.locator('ul li')).toHaveCount(2);
|
||||
|
||||
|
||||
// apply bold
|
||||
await padBody.locator('div').nth(0).selectText();
|
||||
await page.locator('.buttonicon-bold').click()
|
||||
|
||||
await padBody.locator('div').nth(1).selectText();
|
||||
await page.locator('.buttonicon-bold').click()
|
||||
|
||||
await expect(padBody.locator('div b')).toHaveCount(2);
|
||||
|
||||
// outdent first 2 lines
|
||||
await padBody.locator('div').nth(0).selectText();
|
||||
await page.locator('.buttonicon-outdent').click()
|
||||
|
||||
await padBody.locator('div').nth(1).selectText();
|
||||
await page.locator('.buttonicon-outdent').click()
|
||||
|
||||
await expect(padBody.locator('ul li')).toHaveCount(0);
|
||||
|
||||
// check if '*' is displayed
|
||||
const secondLine = padBody.locator('div').nth(1);
|
||||
await expect(secondLine).toHaveText('Second');
|
||||
});
|
||||
|
||||
test('makes text indented and outdented', async function ({page}) {
|
||||
// get the inner iframe
|
||||
|
||||
const padBody = await getPadBody(page);
|
||||
|
||||
// get the first text element out of the inner iframe
|
||||
let firstTextElement = padBody.locator('div').first();
|
||||
|
||||
// select this text element
|
||||
await firstTextElement.selectText()
|
||||
|
||||
// get the indentation button and click it
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
let newFirstTextElement = padBody.locator('div').first();
|
||||
|
||||
// is there a list-indent class element now?
|
||||
await expect(newFirstTextElement.locator('ul')).toHaveCount(1);
|
||||
|
||||
await expect(newFirstTextElement.locator('li')).toHaveCount(1);
|
||||
|
||||
// indent again
|
||||
await page.locator('.buttonicon-indent').click()
|
||||
|
||||
newFirstTextElement = padBody.locator('div').first();
|
||||
|
||||
|
||||
// is there a list-indent class element now?
|
||||
const ulList = newFirstTextElement.locator('ul').first()
|
||||
await expect(ulList).toHaveCount(1);
|
||||
// expect it to be part of a list
|
||||
expect(await ulList.getAttribute('class')).toBe('list-indent2');
|
||||
|
||||
// make sure the text hasn't changed
|
||||
expect(await newFirstTextElement.textContent()).toBe(await firstTextElement.textContent());
|
||||
|
||||
|
||||
// test outdent
|
||||
|
||||
// get the unindentation button and click it twice
|
||||
newFirstTextElement = padBody.locator('div').first();
|
||||
await newFirstTextElement.selectText()
|
||||
await page.locator('.buttonicon-outdent').click()
|
||||
await page.locator('.buttonicon-outdent').click()
|
||||
|
||||
newFirstTextElement = padBody.locator('div').first();
|
||||
|
||||
// is there a list-indent class element now?
|
||||
await expect(newFirstTextElement.locator('ul')).toHaveCount(0);
|
||||
|
||||
// make sure the text hasn't changed
|
||||
expect(await newFirstTextElement.textContent()).toEqual(await firstTextElement.textContent());
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue