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,102 +0,0 @@
'use strict';
describe('change user color', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('Color picker matches original color and remembers the user color' +
' after a refresh', async function () {
this.timeout(10000);
let chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
let $userButton = chrome$('.buttonicon-showusers');
$userButton.trigger('click');
let $userSwatch = chrome$('#myswatch');
$userSwatch.trigger('click');
const fb = chrome$.farbtastic('#colorpicker');
const $colorPickerSave = chrome$('#mycolorpickersave');
let $colorPickerPreview = chrome$('#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($colorPickerPreview.css('background-color')).to.be($userSwatch.css('background-color'));
// The swatch updates as the test color is picked.
fb.setColor(testColorHash);
expect($colorPickerPreview.css('background-color')).to.be(testColorRGB);
$colorPickerSave.trigger('click');
expect($userSwatch.css('background-color')).to.be(testColorRGB);
// give it a second to save the color on the server side
await new Promise((resolve) => setTimeout(resolve, 1000));
// get a new pad, but don't clear the cookies
await helper.aNewPad({clearCookies: false});
chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
$userButton = chrome$('.buttonicon-showusers');
$userButton.trigger('click');
$userSwatch = chrome$('#myswatch');
$userSwatch.trigger('click');
$colorPickerPreview = chrome$('#mycolorpickerpreview');
expect($colorPickerPreview.css('background-color')).to.be(testColorRGB);
expect($userSwatch.css('background-color')).to.be(testColorRGB);
});
it('Own user color is shown when you enter a chat', function (done) {
this.timeout(1000);
const chrome$ = helper.padChrome$;
const $colorOption = helper.padChrome$('#options-colorscheck');
if (!$colorOption.is(':checked')) {
$colorOption.trigger('click');
}
// click on the settings button to make settings visible
const $userButton = chrome$('.buttonicon-showusers');
$userButton.trigger('click');
const $userSwatch = chrome$('#myswatch');
$userSwatch.trigger('click');
const fb = chrome$.farbtastic('#colorpicker');
const $colorPickerSave = chrome$('#mycolorpickersave');
// Same color represented in two different ways
const testColorHash = '#abcdef';
const testColorRGB = 'rgb(171, 205, 239)';
fb.setColor(testColorHash);
$colorPickerSave.trigger('click');
// click on the chat button to make chat visible
const $chatButton = chrome$('#chaticon');
$chatButton.trigger('click');
const $chatInput = chrome$('#chatinput');
$chatInput.sendkeys('O hi'); // simulate a keypress of typing user
$chatInput.sendkeys('{enter}');
// wait until the chat message shows up
helper.waitFor(() => chrome$('#chattext').children('p').length !== 0).done(() => {
const $firstChatMessage = chrome$('#chattext').children('p');
// expect the first chat message to be of the user's color
expect($firstChatMessage.css('background-color')).to.be(testColorRGB);
done();
});
});
});

View file

@ -1,31 +0,0 @@
'use strict';
describe('font select', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('makes text RobotoMono', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.trigger('click');
// get the font menu and RobotoMono option
const $viewfontmenu = chrome$('#viewfontmenu');
// select RobotoMono and fire change event
// $RobotoMonooption.attr('selected','selected');
// commenting out above will break safari test
$viewfontmenu.val('RobotoMono');
$viewfontmenu.trigger('change');
// check if font changed to RobotoMono
const fontFamily = inner$('body').css('font-family').toLowerCase();
const containsStr = fontFamily.indexOf('robotomono');
expect(containsStr).to.not.be(-1);
});
});

View file

@ -167,7 +167,7 @@ describe('the test helper', function () {
expect(Date.now() - before).to.be.lessThan(800);
});
it('polls exactly once if timeout < interval', async function () {
xit('polls exactly once if timeout < interval', async function () {
let calls = 0;
await helper.waitFor(() => { calls++; }, 1, 1000)
.fail(() => {}) // Suppress the redundant uncatchable exception.
@ -249,7 +249,7 @@ describe('the test helper', function () {
});
});
it('changes editor selection to be between startOffset of $startLine ' +
xit('changes editor selection to be between startOffset of $startLine ' +
'and endOffset of $endLine', function (done) {
const inner$ = helper.padInner$;

View file

@ -1,31 +0,0 @@
'use strict';
describe('height regression after ace.js refactoring', function () {
before(async function () {
await helper.aNewPad();
});
// everything fits inside the viewport
it('clientHeight should equal scrollHeight with few lines', async function () {
await helper.clearPad();
const outerHtml = helper.padChrome$('iframe')[0].contentDocument.documentElement;
// Give some time for the heights to settle.
await new Promise((resolve) => setTimeout(resolve, 100));
expect(outerHtml.clientHeight).to.be(outerHtml.scrollHeight);
});
it('client height should be less than scrollHeight with many lines', async function () {
await helper.clearPad();
await helper.edit('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 outerHtml = helper.padChrome$('iframe')[0].contentDocument.documentElement;
// Need to poll because the heights take some time to settle.
await helper.waitForPromise(() => outerHtml.clientHeight < outerHtml.scrollHeight);
});
});

View file

@ -1,62 +0,0 @@
'use strict';
describe('italic some text', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('makes text italic using button', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
// get the bold button and click it
const $boldButton = chrome$('.buttonicon-italic');
$boldButton.trigger('click');
// ace creates a new dom element when you press a button, just get the first text element again
const $newFirstTextElement = inner$('div').first();
// is there a <i> element now?
const isItalic = $newFirstTextElement.find('i').length === 1;
// expect it to be bold
expect(isItalic).to.be(true);
// make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
});
it('makes text italic using keypress', async function () {
const inner$ = helper.padInner$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
const e = new inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 105; // i
inner$('#innerdocbody').trigger(e);
// ace creates a new dom element when you press a button, just get the first text element again
const $newFirstTextElement = inner$('div').first();
// is there a <i> element now?
const isItalic = $newFirstTextElement.find('i').length === 1;
// expect it to be bold
expect(isItalic).to.be(true);
// make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
});
});

View file

@ -1,121 +0,0 @@
'use strict';
describe('Language select and change', function () {
// Destroy language cookies
window.Cookies.remove('language');
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
// Destroy language cookies
it('makes text german', async function () {
this.timeout(1000);
const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.trigger('click');
// click the language button
const $language = chrome$('#languagemenu');
const $languageoption = $language.find('[value=de]');
// select german
$languageoption.attr('selected', 'selected');
$language.trigger('change');
await helper.waitForPromise(
() => chrome$('.buttonicon-bold').parent()[0].title === 'Fett (Strg-B)');
// get the value of the bold button
const $boldButton = chrome$('.buttonicon-bold').parent();
// get the title of the bold button
const boldButtonTitle = $boldButton[0].title;
// check if the language is now german
expect(boldButtonTitle).to.be('Fett (Strg-B)');
});
it('makes text English', async function () {
this.timeout(1000);
const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.trigger('click');
// click the language button
const $language = chrome$('#languagemenu');
// select english
$language.val('en');
$language.trigger('change');
// get the value of the bold button
let $boldButton = chrome$('.buttonicon-bold').parent();
await helper.waitForPromise(() => $boldButton[0].title !== 'Fett (Strg+B)');
// get the value of the bold button
$boldButton = chrome$('.buttonicon-bold').parent();
// get the title of the bold button
const boldButtonTitle = $boldButton[0].title;
// check if the language is now English
expect(boldButtonTitle).to.be('Bold (Ctrl+B)');
});
it('changes direction when picking an rtl lang', async function () {
// TODO: flaky
if (window.bowser.safari) {
this.timeout(5000);
} else {
this.timeout(1000);
}
const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.trigger('click');
// click the language button
const $language = chrome$('#languagemenu');
const $languageoption = $language.find('[value=ar]');
// select arabic
// $languageoption.attr('selected','selected'); // Breaks the test..
$language.val('ar');
$languageoption.trigger('change');
await helper.waitForPromise(() => chrome$('html')[0].dir !== 'ltr');
// check if the document's direction was changed
expect(chrome$('html')[0].dir).to.be('rtl');
});
it('changes direction when picking an ltr lang', async function () {
const chrome$ = helper.padChrome$;
// click on the settings button to make settings visible
const $settingsButton = chrome$('.buttonicon-settings');
$settingsButton.trigger('click');
// click the language button
const $language = chrome$('#languagemenu');
const $languageoption = $language.find('[value=en]');
// select english
// select arabic
$languageoption.attr('selected', 'selected');
$language.val('en');
$languageoption.trigger('change');
await helper.waitForPromise(() => chrome$('html')[0].dir !== 'rtl');
// check if the document's direction was changed
expect(chrome$('html')[0].dir).to.be('ltr');
});
});

View file

@ -1,233 +0,0 @@
'use strict';
describe('ordered_list.js', function () {
describe('assign ordered list', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('inserts ordered list text', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.trigger('click');
await helper.waitForPromise(() => inner$('div').first().find('ol li').length === 1);
});
context('when user presses Ctrl+Shift+N', function () {
context('and pad shortcut is enabled', function () {
beforeEach(async function () {
const originalHTML = helper.padInner$('body').html();
makeSureShortcutIsEnabled('cmdShiftN');
triggerCtrlShiftShortcut('N');
await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML);
});
it('inserts unordered list', async function () {
await helper.waitForPromise(
() => helper.padInner$('div').first().find('ol li').length === 1);
});
});
context('and pad shortcut is disabled', function () {
beforeEach(async function () {
const originalHTML = helper.padInner$('body').html();
makeSureShortcutIsDisabled('cmdShiftN');
triggerCtrlShiftShortcut('N');
try {
// The HTML should not change. Briefly wait for it to change and fail if it does change.
await helper.waitForPromise(
() => helper.padInner$('body').html() !== originalHTML, 500);
} catch (err) {
// We want the test to pass if the above wait timed out. (If it timed out that
// means the HTML never changed, which is a good thing.)
// TODO: Re-throw non-"condition never became true" errors to avoid false positives.
}
// This will fail if the above `waitForPromise()` succeeded.
expect(helper.padInner$('body').html()).to.be(originalHTML);
});
it('does not insert unordered list', async function () {
this.timeout(3000);
try {
await helper.waitForPromise(
() => helper.padInner$('div').first().find('ol li').length === 1);
} catch (err) {
return;
}
expect().fail('Unordered list inserted, should ignore shortcut');
});
});
});
context('when user presses Ctrl+Shift+1', function () {
context('and pad shortcut is enabled', function () {
beforeEach(async function () {
const originalHTML = helper.padInner$('body').html();
makeSureShortcutIsEnabled('cmdShift1');
triggerCtrlShiftShortcut('1');
await helper.waitForPromise(() => helper.padInner$('body').html() !== originalHTML);
});
it('inserts unordered list', async function () {
helper.waitForPromise(() => helper.padInner$('div').first().find('ol li').length === 1);
});
});
context('and pad shortcut is disabled', function () {
beforeEach(async function () {
const originalHTML = helper.padInner$('body').html();
makeSureShortcutIsDisabled('cmdShift1');
triggerCtrlShiftShortcut('1');
try {
// The HTML should not change. Briefly wait for it to change and fail if it does change.
await helper.waitForPromise(
() => helper.padInner$('body').html() !== originalHTML, 500);
} catch (err) {
// We want the test to pass if the above wait timed out. (If it timed out that
// means the HTML never changed, which is a good thing.)
// TODO: Re-throw non-"condition never became true" errors to avoid false positives.
}
// This will fail if the above `waitForPromise()` succeeded.
expect(helper.padInner$('body').html()).to.be(originalHTML);
});
it('does not insert unordered list', async function () {
this.timeout(3000);
try {
await helper.waitForPromise(
() => helper.padInner$('div').first().find('ol li').length === 1);
} catch (err) {
return;
}
expect().fail('Unordered list inserted, should ignore shortcut');
});
});
});
it('issue #4748 keeps numbers increment on OL', async function () {
this.timeout(5000);
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
const $firstLine = inner$('div').first();
$firstLine.sendkeys('{selectall}');
$insertorderedlistButton.trigger('click');
const $secondLine = inner$('div').first().next();
$secondLine.sendkeys('{selectall}');
$insertorderedlistButton.trigger('click');
expect($secondLine.find('ol').attr('start') === 2);
});
xit('issue #1125 keeps the numbered list on enter for the new line', async function () {
// EMULATES PASTING INTO A PAD
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.trigger('click');
// type a bit, make a line break and type again
const $firstTextElement = inner$('div span').first();
$firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}');
await helper.waitForPromise(() => inner$('div span').first().text().indexOf('line 2') === -1);
const $newSecondLine = inner$('div').first().next();
const hasOLElement = $newSecondLine.find('ol li').length === 1;
expect(hasOLElement).to.be(true);
expect($newSecondLine.text()).to.be('line 2');
const hasLineNumber = $newSecondLine.find('ol').attr('start') === 2;
// This doesn't work because pasting in content doesn't work
expect(hasLineNumber).to.be(true);
});
const triggerCtrlShiftShortcut = (shortcutChar) => {
const inner$ = helper.padInner$;
const e = new inner$.Event(helper.evtType);
e.ctrlKey = true;
e.shiftKey = true;
e.which = shortcutChar.toString().charCodeAt(0);
inner$('#innerdocbody').trigger(e);
};
const makeSureShortcutIsDisabled = (shortcut) => {
helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = false;
};
const makeSureShortcutIsEnabled = (shortcut) => {
helper.padChrome$.window.clientVars.padShortcutEnabled[shortcut] = true;
};
});
describe('Pressing Tab in an OL increases and decreases indentation', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('indent and de-indent list item with keypress', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.trigger('click');
const e = new inner$.Event(helper.evtType);
e.keyCode = 9; // tab
inner$('#innerdocbody').trigger(e);
expect(inner$('div').first().find('.list-number2').length === 1).to.be(true);
e.shiftKey = true; // shift
e.keyCode = 9; // tab
inner$('#innerdocbody').trigger(e);
await helper.waitForPromise(() => inner$('div').first().find('.list-number1').length === 1);
});
});
describe('Pressing indent/outdent button in an OL increases and ' +
'decreases indentation and bullet / ol formatting', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('indent and de-indent list item with indent button', async function () {
this.timeout(1000);
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
const $insertorderedlistButton = chrome$('.buttonicon-insertorderedlist');
$insertorderedlistButton.trigger('click');
const $indentButton = chrome$('.buttonicon-indent');
$indentButton.trigger('click'); // make it indented twice
expect(inner$('div').first().find('.list-number2').length === 1).to.be(true);
const $outdentButton = chrome$('.buttonicon-outdent');
$outdentButton.trigger('click'); // make it deindented to 1
await helper.waitForPromise(() => inner$('div').first().find('.list-number1').length === 1);
});
});
});

View file

@ -1,59 +0,0 @@
'use strict';
describe('undo button then redo button', function () {
beforeEach(async function () {
await helper.aNewPad();
});
it('redo some typing with button', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element inside the editable space
const $firstTextElement = inner$('div span').first();
const originalValue = $firstTextElement.text(); // get the original value
const newString = 'Foo';
$firstTextElement.sendkeys(newString); // send line 1 to the pad
const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get undo and redo buttons
const $undoButton = chrome$('.buttonicon-undo');
const $redoButton = chrome$('.buttonicon-redo');
// click the buttons
$undoButton.trigger('click'); // removes foo
$redoButton.trigger('click'); // resends foo
await helper.waitForPromise(() => inner$('div span').first().text() === newString);
const finalValue = inner$('div').first().text();
expect(finalValue).to.be(modifiedValue); // expect the value to change
});
it('redo some typing with keypress', async function () {
const inner$ = helper.padInner$;
// get the first text element inside the editable space
const $firstTextElement = inner$('div span').first();
const originalValue = $firstTextElement.text(); // get the original value
const newString = 'Foo';
$firstTextElement.sendkeys(newString); // send line 1 to the pad
const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
let e = new inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$('#innerdocbody').trigger(e);
e = new inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 121; // y
inner$('#innerdocbody').trigger(e);
await helper.waitForPromise(() => inner$('div span').first().text() === newString);
const finalValue = inner$('div').first().text();
expect(finalValue).to.be(modifiedValue); // expect the value to change
});
});

View file

@ -1,35 +0,0 @@
'use strict';
describe('strikethrough button', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('makes text strikethrough', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
// get the strikethrough button and click it
const $strikethroughButton = chrome$('.buttonicon-strikethrough');
$strikethroughButton.trigger('click');
// ace creates a new dom element when you press a button, just get the first text element again
const $newFirstTextElement = inner$('div').first();
// is there a <i> element now?
const isstrikethrough = $newFirstTextElement.find('s').length === 1;
// expect it to be strikethrough
expect(isstrikethrough).to.be(true);
// make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
});
});

View file

@ -1,41 +0,0 @@
'use strict';
// deactivated, we need a nice way to get the timeslider, this is ugly
xdescribe('timeslider button takes you to the timeslider of a pad', function () {
beforeEach(async function () {
await helper.aNewPad();
});
it('timeslider contained in URL', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element inside the editable space
const $firstTextElement = inner$('div span').first();
const originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys('Testing'); // send line 1 to the pad
const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// The value has changed so we can..
await helper.waitForPromise(() => modifiedValue !== originalValue);
const $timesliderButton = chrome$('#timesliderlink');
$timesliderButton.trigger('click'); // So click the timeslider link
await helper.waitForPromise(() => {
const iFrameURL = chrome$.window.location.href;
if (iFrameURL) {
return iFrameURL.indexOf('timeslider') !== -1;
} else {
return false; // the URL hasnt been set yet
}
});
// click the buttons
const iFrameURL = chrome$.window.location.href; // get the url
const inTimeslider = iFrameURL.indexOf('timeslider') !== -1;
expect(inTimeslider).to.be(true); // expect the value to change
});
});

View file

@ -1,101 +0,0 @@
'use strict';
describe('timeslider follow', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
// TODO needs test if content is also followed, when user a makes edits
// while user b is in the timeslider
it("content as it's added to timeslider", async function () {
this.timeout(20000);
// 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 helper.edit(message, newLines * i + 1);
}
await helper.gotoTimeslider(0);
await helper.waitForPromise(() => helper.contentWindow().location.hash === '#0');
const originalTop = helper.contentWindow().$('#innerdocbody').offset();
// set to follow contents as it arrives
helper.contentWindow().$('#options-followContents').prop('checked', true);
helper.contentWindow().$('#playpause_button_icon').trigger('click');
let newTop;
await helper.waitForPromise(() => {
newTop = helper.contentWindow().$('#innerdocbody').offset();
return newTop.top < originalTop.top;
});
});
/**
* 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.
*/
it('only to lines that exist in the pad view, regression test for #4389', async function () {
await helper.clearPad();
await helper.edit('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 helper.edit('Another test line', 40);
await helper.gotoTimeslider();
// set to follow contents as it arrives
helper.contentWindow().$('#options-followContents').prop('checked', true);
const oldYPosition = helper.contentWindow().$('#editorcontainerbox')[0].scrollTop;
expect(oldYPosition).to.be(0);
/**
* pad content rev 0 [default Pad text]
* pad content rev 1 ['']
* pad content rev 2 ['Test line','','', ..., '']
* pad content rev 3 ['Test line','',..., 'Another test line', ..., '']
*/
// line 40 changed
helper.contentWindow().$('#leftstep').trigger('click');
await helper.waitForPromise(() => hasFollowedToLine(40));
// line 1 is the first line that changed
helper.contentWindow().$('#leftstep').trigger('click');
await helper.waitForPromise(() => hasFollowedToLine(1));
// line 1 changed
helper.contentWindow().$('#leftstep').trigger('click');
await helper.waitForPromise(() => hasFollowedToLine(1));
// line 1 changed
helper.contentWindow().$('#rightstep').trigger('click');
await helper.waitForPromise(() => hasFollowedToLine(1));
// line 1 is the first line that changed
helper.contentWindow().$('#rightstep').trigger('click');
await helper.waitForPromise(() => hasFollowedToLine(1));
// line 40 changed
helper.contentWindow().$('#rightstep').trigger('click');
helper.waitForPromise(() => hasFollowedToLine(40));
});
});
/**
* @param {number} lineNum
* @returns {boolean} scrolled to the lineOffset?
*/
const hasFollowedToLine = (lineNum) => {
const scrollPosition = helper.contentWindow().$('#editorcontainerbox')[0].scrollTop;
const lineOffset =
helper.contentWindow().$('#innerdocbody').find(`div:nth-child(${lineNum})`)[0].offsetTop;
return Math.abs(scrollPosition - lineOffset) < 1;
};

View file

@ -1,46 +0,0 @@
'use strict';
describe('undo button', function () {
beforeEach(async function () {
await helper.aNewPad();
});
it('undo some typing by clicking undo button', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element inside the editable space
const $firstTextElement = inner$('div span').first();
const originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys('foo'); // send line 1 to the pad
const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get clear authorship button as a variable
const $undoButton = chrome$('.buttonicon-undo');
// click the button
$undoButton.trigger('click');
await helper.waitForPromise(() => inner$('div span').first().text() === originalValue);
});
it('undo some typing using a keypress', async function () {
const inner$ = helper.padInner$;
// get the first text element inside the editable space
const $firstTextElement = inner$('div span').first();
const originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys('foo'); // send line 1 to the pad
const modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
const e = new inner$.Event(helper.evtType);
e.ctrlKey = true; // Control key
e.which = 90; // z
inner$('#innerdocbody').trigger(e);
await helper.waitForPromise(() => inner$('div span').first().text() === originalValue);
});
});

View file

@ -1,146 +0,0 @@
'use strict';
describe('unordered_list.js', function () {
describe('assign unordered list', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('insert unordered list text then removes by outdent', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const originalText = inner$('div').first().text();
const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist');
$insertunorderedlistButton.trigger('click');
await helper.waitForPromise(() => {
const newText = inner$('div').first().text();
return newText === originalText && inner$('div').first().find('ul li').length === 1;
});
// remove indentation by bullet and ensure text string remains the same
chrome$('.buttonicon-outdent').trigger('click');
await helper.waitForPromise(() => inner$('div').first().text() === originalText);
});
});
describe('unassign unordered list', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('insert unordered list text then remove by clicking list again', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const originalText = inner$('div').first().text();
const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist');
$insertunorderedlistButton.trigger('click');
await helper.waitForPromise(() => {
const newText = inner$('div').first().text();
return newText === originalText && inner$('div').first().find('ul li').length === 1;
});
// remove indentation by bullet and ensure text string remains the same
$insertunorderedlistButton.trigger('click');
await helper.waitForPromise(() => inner$('div').find('ul').length !== 1);
});
});
describe('keep unordered list on enter key', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('Keeps the unordered list on enter for the new line', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist');
$insertorderedlistButton.trigger('click');
// type a bit, make a line break and type again
const $firstTextElement = inner$('div span').first();
$firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}');
await helper.waitForPromise(() => inner$('div span').first().text().indexOf('line 2') === -1);
const $newSecondLine = inner$('div').first().next();
const hasULElement = $newSecondLine.find('ul li').length === 1;
expect(hasULElement).to.be(true);
expect($newSecondLine.text()).to.be('line 2');
});
});
describe('Pressing Tab in an UL increases and decreases indentation', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('indent and de-indent list item with keypress', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
const $insertorderedlistButton = chrome$('.buttonicon-insertunorderedlist');
$insertorderedlistButton.trigger('click');
const e = new inner$.Event(helper.evtType);
e.keyCode = 9; // tab
inner$('#innerdocbody').trigger(e);
expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true);
e.shiftKey = true; // shift
e.keyCode = 9; // tab
inner$('#innerdocbody').trigger(e);
await helper.waitForPromise(() => inner$('div').first().find('.list-bullet1').length === 1);
});
});
describe('Pressing indent/outdent button in an UL increases and decreases indentation ' +
'and bullet / ol formatting', function () {
// create a new pad before each test run
beforeEach(async function () {
await helper.aNewPad();
});
it('indent and de-indent list item with indent button', async function () {
const inner$ = helper.padInner$;
const chrome$ = helper.padChrome$;
// get the first text element out of the inner iframe
const $firstTextElement = inner$('div').first();
// select this text element
$firstTextElement.sendkeys('{selectall}');
const $insertunorderedlistButton = chrome$('.buttonicon-insertunorderedlist');
$insertunorderedlistButton.trigger('click');
const $indentButton = chrome$('.buttonicon-indent');
$indentButton.trigger('click'); // make it indented twice
expect(inner$('div').first().find('.list-bullet2').length === 1).to.be(true);
const $outdentButton = chrome$('.buttonicon-outdent');
$outdentButton.trigger('click'); // make it deindented to 1
await helper.waitForPromise(() => inner$('div').first().find('.list-bullet1').length === 1);
});
});
});

View file

@ -1,56 +0,0 @@
'use strict';
describe('urls', function () {
// Returns the first text element. Note that any change to the text element will result in the
// element being replaced with another object.
const txt = () => helper.padInner$('div').first();
before(async function () {
await helper.aNewPad();
});
beforeEach(async function () {
await helper.clearPad();
});
describe('entering a URL makes a link', function () {
for (const url of ['https://etherpad.org', 'www.etherpad.org']) {
it(url, async function () {
this.timeout(5000);
const url = 'https://etherpad.org';
await helper.edit(url);
await helper.waitForPromise(() => txt().find('a').length === 1, 2000);
const link = txt().find('a');
expect(link.attr('href')).to.be(url);
expect(link.text()).to.be(url);
});
}
});
describe('special characters inside URL', function () {
for (const char of '-:@_.,~%+/?=&#!;()[]$\'*') {
const url = `https://etherpad.org/${char}foo`;
it(url, async function () {
await helper.edit(url);
await helper.waitForPromise(() => txt().find('a').length === 1);
const link = txt().find('a');
expect(link.attr('href')).to.be(url);
expect(link.text()).to.be(url);
});
}
});
describe('punctuation after URL is ignored', function () {
for (const char of ':.,;?!)]\'*') {
const want = 'https://etherpad.org';
const input = want + char;
it(input, async function () {
await helper.edit(input);
await helper.waitForPromise(() => txt().find('a').length === 1);
const link = txt().find('a');
expect(link.attr('href')).to.be(want);
expect(link.text()).to.be(want);
});
}
});
});