Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make spellchecker work for multi line quotes #4518

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8592cc3
drop intermediate exports
tomaskikutis May 2, 2024
a89a7e7
v1
tomaskikutis May 7, 2024
1624796
move code to reload spellchecker warnings after action is performed
tomaskikutis May 7, 2024
87157f7
move spellcheck call componentDidUpdate
tomaskikutis May 7, 2024
c5d755f
enable accepting spellchecker suggestions inside table cell
tomaskikutis May 8, 2024
6e2620e
improve interface of getDecorators
tomaskikutis May 8, 2024
7f224c8
disable spellchecking for tables
tomaskikutis May 8, 2024
39b0ad4
when spellchecking action is performed - update UI without delay
tomaskikutis May 8, 2024
6ad6a66
drop spellchecker initialization in authoring-react
tomaskikutis May 8, 2024
b8ecc43
remove unused import
tomaskikutis May 8, 2024
1ba6944
fix unit tests
tomaskikutis May 9, 2024
1c3e597
replace removed unit tests with e2e tests
tomaskikutis May 9, 2024
1896dd4
fix component getting stuck in permanent loading state
tomaskikutis May 10, 2024
7055860
fix unit test
tomaskikutis May 12, 2024
b0d91ab
port broken e2e tests to playwright
tomaskikutis May 14, 2024
dbb6d20
fix record name
tomaskikutis May 14, 2024
65019be
fix lint
tomaskikutis May 14, 2024
1016a06
fix unit test
tomaskikutis May 14, 2024
f5ee8bd
make e2e test more stable
tomaskikutis May 14, 2024
e89226f
Merge branch 'develop' into make-spellchecker-work-for-multi-line-quotes
tomaskikutis May 14, 2024
8a6a715
make e2e tests more stable
tomaskikutis May 14, 2024
6706c6c
see if 100ms delay is enough
tomaskikutis May 14, 2024
5cc938e
Revert "see if 100ms delay is enough"
tomaskikutis May 14, 2024
23b5634
Merge branch 'develop' into make-spellchecker-work-for-multi-line-quotes
tomaskikutis May 15, 2024
cd80fc4
try lowering the delay again
tomaskikutis May 15, 2024
2f12177
fix unit test
tomaskikutis May 15, 2024
9167c28
fix unit test
tomaskikutis May 15, 2024
34d07a1
disable preference tests
tomaskikutis May 15, 2024
e42d312
use spec reported to be able to see which test throws
tomaskikutis May 15, 2024
dbd0987
Revert "disable preference tests"
tomaskikutis May 15, 2024
3434459
fix unit tests v10
tomaskikutis May 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions e2e/client/playwright/editor3.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import {test, expect} from '@playwright/test';
import {Monitoring} from './page-object-models/monitoring';
import {restoreDatabaseSnapshot, s} from './utils';
import {getEditor3Paragraphs} from './utils/editor3';

test('accepting a spelling suggestion', async ({page}) => {
await restoreDatabaseSnapshot({snapshotName: 'spellchecker'});

await page.goto('/#/workspace/monitoring');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=spellchecker test'),
).dblclick();

await expect(page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning'))).toHaveCount(2);

expect(await getEditor3Paragraphs(page.locator(s('authoring', 'authoring-field=body_html'))))
.toStrictEqual(['ghello world', 'ghello world']);

await page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning'))
.first()
.click({button: 'right'});

await page.locator(s('spellchecker-menu')).getByRole('button', {name: 'hello'}).click();

await expect(
(await page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning')).all()).length,
).toBe(1);

expect(await getEditor3Paragraphs(page.locator(s('authoring', 'authoring-field=body_html'))))
.toStrictEqual(['hello world', 'ghello world']);
});

test('adding word marked as a spellchecker issue to dictionary', async ({page}) => {
await restoreDatabaseSnapshot({snapshotName: 'spellchecker'});

await page.goto('/#/workspace/monitoring');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=spellchecker test'),
).dblclick();

await expect(page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning'))).toHaveCount(2);

expect(await getEditor3Paragraphs(page.locator(s('authoring', 'authoring-field=body_html'))))
.toStrictEqual(['ghello world', 'ghello world']);

await page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning'))
.first()
.click({button: 'right'});

await page.locator(s('spellchecker-menu')).getByRole('button', {name: 'Add to dictionary'}).click();

/**
* it expects zero, because when a word is added to dictionary
* it should remove warnings for all instances of that word
* including nested editors (e.g. multi-line-quote)
*/
await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'spellchecker-warning')),
).toHaveCount(0);

expect(await getEditor3Paragraphs(page.locator(s('authoring', 'authoring-field=body_html'))))
.toStrictEqual(['ghello world', 'ghello world']);
});

/**
* FYI undo/redo isn't working the same as in the main editor outside tables
* and it's not great that it's character based.
*/
test('tables maintaining cursor position at the start when executing "undo" action', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot({snapshotName: 'editor3-tables'});

await page.goto('/#/workspace/monitoring');

await monitoring.selectDeskOrWorkspace('Sports');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=test sports story'),
).dblclick();

await page.locator(
s('authoring', 'authoring-field=body_html', 'toolbar'),
).getByRole('button', {name: 'table'}).click();

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('foo', {delay: 100});

await page.keyboard.press('ArrowLeft');
await page.keyboard.press('ArrowLeft');
await page.keyboard.press('ArrowLeft');
await page.keyboard.press('Control+z');

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('bar', {delay: 100});

await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'table-block')).locator('[contenteditable]').first(),
).toHaveText('barfo');
});

/**
* FYI undo/redo isn't working the same as in the main editor outside tables
* and it's not great that it's character based.
*/
test('tables maintaining cursor position in the middle when executing "undo" action', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot({snapshotName: 'editor3-tables'});

await page.goto('/#/workspace/monitoring');

await monitoring.selectDeskOrWorkspace('Sports');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=test sports story'),
).dblclick();

await page.locator(
s('authoring', 'authoring-field=body_html', 'toolbar'),
).getByRole('button', {name: 'table'}).click();

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('foo', {delay: 100});

await page.keyboard.press('ArrowLeft');
await page.keyboard.press('ArrowLeft');
await page.keyboard.press('Control+z');
await page.keyboard.press('Control+z');

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('bar', {delay: 100});

await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'table-block')).locator('[contenteditable]').first(),
).toHaveText('fbar');
});

/**
* FYI undo/redo isn't working the same as in the main editor outside tables
* and it's not great that it's character based.
*/
test('tables maintaining cursor position at the end when executing "undo" action', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot({snapshotName: 'editor3-tables'});

await page.goto('/#/workspace/monitoring');

await monitoring.selectDeskOrWorkspace('Sports');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=test sports story'),
).dblclick();

await page.locator(
s('authoring', 'authoring-field=body_html', 'toolbar'),
).getByRole('button', {name: 'table'}).click();

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('foo', {delay: 100});

await page.keyboard.press('Control+z'); // undo last character

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('bar', {delay: 100});

await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'table-block')).locator('[contenteditable]').first(),
).toHaveText('fobar');
});

/**
* FYI undo/redo isn't working the same as in the main editor outside tables
* and it's not great that it's character based.
*/
test('tables maintaining cursor position when executing "redo" action', async ({page}) => {
const monitoring = new Monitoring(page);

await restoreDatabaseSnapshot({snapshotName: 'editor3-tables'});

await page.goto('/#/workspace/monitoring');

await monitoring.selectDeskOrWorkspace('Sports');

await page.locator(
s('monitoring-group=Sports / Working Stage', 'article-item=test sports story'),
).dblclick();

await page.locator(
s('authoring', 'authoring-field=body_html', 'toolbar'),
).getByRole('button', {name: 'table'}).click();

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('foo', {delay: 100});

await page.keyboard.press('Control+z');
await page.keyboard.press('Control+y');

await page.locator(s('authoring', 'authoring-field=body_html', 'table-block'))
.locator('[contenteditable]').first().pressSequentially('bar', {delay: 100});

await expect(
page.locator(s('authoring', 'authoring-field=body_html', 'table-block')).locator('[contenteditable]').first(),
).toHaveText('fobaro');
});
11 changes: 11 additions & 0 deletions e2e/client/playwright/utils/editor3.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Locator} from '@playwright/test';

export function getEditor3Paragraphs(field: Locator): Promise<Array<string>> {
return field.locator('.DraftEditor-root')
.first() // there might be multiple roots when working with nested blocks e.g. multi-line-quote
.locator('[data-contents="true"]')
.first() // there might be multiple [data-contents] when working with nested blocks e.g. multi-line-quote
.locator('> *')
.allInnerTexts()
.then((items) => items.filter((text) => text.trim().length > 0));
}
71 changes: 0 additions & 71 deletions e2e/client/specs/editor3_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ describe('editor3', () => {
const bodyEditor = new Editor3(editors.get(1));
const headlineEditor = new Editor3(editors.get(0));
const selectAll = protractor.Key.chord(protractor.Key.SHIFT, protractor.Key.UP);
const tableEl = editors.get(1).element(by.className('table-block'));

beforeEach(() => {
monitoring.openMonitoring();
Expand Down Expand Up @@ -89,76 +88,6 @@ describe('editor3', () => {
expect(body.element(by.tagName('a')).getAttribute('href')).toBe('https://example.com/');
});

it('can add tables', () => {
const tableEditor = new Editor3(tableEl);

bodyEditor.toolbar.table();
tableEditor.sendKeys('foo');

const body = getPreviewBody();

expect(body.element(by.tagName('table')).getText()).toBe('foo');
});

it('ctrl+z on tables mantains cursor position at the end', () => {
const tableEditor = new Editor3(tableEl);

bodyEditor.toolbar.table();
tableEditor.sendKeys('foo');
tableEditor.sendKeys(protractor.Key.CONTROL, 'z');
tableEditor.sendKeys('bar');

const body = getPreviewBody();

expect(body.element(by.tagName('table')).getText()).toBe('fobar');
});

it('ctrl+z on tables mantains cursor position at the beginning', () => {
const tableEditor = new Editor3(editors.get(1).element(by.className('table-block')));

bodyEditor.toolbar.table();
tableEditor.sendKeys('foo');
tableEditor.sendKeys(protractor.Key.ARROW_LEFT);
tableEditor.sendKeys(protractor.Key.ARROW_LEFT);
tableEditor.sendKeys(protractor.Key.ARROW_LEFT);
tableEditor.sendKeys(protractor.Key.CONTROL, 'z');
tableEditor.sendKeys('bar');

const body = getPreviewBody();

expect(body.element(by.tagName('table')).getText()).toBe('barfo');
});

it('ctrl+z on tables mantains cursor position in the middle', () => {
const tableEditor = new Editor3(editors.get(1).element(by.className('table-block')));

bodyEditor.toolbar.table();
tableEditor.sendKeys('foo');
tableEditor.sendKeys(protractor.Key.ARROW_LEFT);
tableEditor.sendKeys(protractor.Key.ARROW_LEFT);
tableEditor.sendKeys(protractor.Key.CONTROL, 'z');
tableEditor.sendKeys(protractor.Key.CONTROL, 'z');
tableEditor.sendKeys('bar');

const body = getPreviewBody();

expect(body.element(by.tagName('table')).getText()).toBe('fbar');
});

it('ctrl+y on tables mantains cursor position', () => {
const tableEditor = new Editor3(editors.get(1).element(by.className('table-block')));

bodyEditor.toolbar.table();
tableEditor.sendKeys('foo');
tableEditor.sendKeys(protractor.Key.CONTROL, 'z');
tableEditor.sendKeys(protractor.Key.CONTROL, 'y');
tableEditor.sendKeys('bar');

const body = getPreviewBody();

expect(body.element(by.tagName('table')).getText()).toBe('fobaro');
});

function getPreviewBody() {
authoring.save();
assertToastMsg('success', 'Item updated.');
Expand Down
1 change: 1 addition & 0 deletions e2e/server/dump/records/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
editor3-tables only adds tables formatting option to story content profile. It can be merged to main. The only reason it was added as a separate record is to avoid merge conflicts on open PRs.
Binary file added e2e/server/dump/records/editor3-tables.json.bz2
Binary file not shown.
Binary file added e2e/server/dump/records/spellchecker.json.bz2
Binary file not shown.
6 changes: 4 additions & 2 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ var path = require('path');
var grunt = require('grunt');
var makeConfig = require('./webpack.config.js');

process.env.TZ = "Europe/Prague";
require('karma-spec-reporter');

process.env.TZ = 'Europe/Prague';

module.exports = function(config) {
var webpackConfig = makeConfig(grunt);
Expand Down Expand Up @@ -54,7 +56,7 @@ module.exports = function(config) {
},

// test results reporter to use
reporters: ['dots'],
reporters: ['spec'],

// web server port
port: 8080,
Expand Down
9 changes: 9 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"karma-jasmine": "^1.1.1",
"karma-ng-html2js-preprocessor": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-spec-reporter": "0.0.36",
"karma-webpack": "^2.0.13",
"react-addons-test-utils": "^15.6.0",
"react-test-renderer": "^16.13.1",
Expand Down
Loading
Loading