Skip to content

Commit 22487e4

Browse files
author
billy clark
committed
Merge branch 'off-cycle-release'
2 parents 29aca5e + 4cc056a commit 22487e4

40 files changed

+382
-201
lines changed

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# The .dockerignore file excludes files from the container build process.
2+
#
3+
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
4+
5+
# Exclude locally vendored dependencies.
6+
src/vendor/
7+
8+
# Exclude "build-time" ignore files.
9+
.dockerignore
10+
11+
# Exclude git history and configuration.
12+
.gitignore
13+
.git

.github/pull_request_template.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ Please provide screenshots / animations for any change that involves the UI. Ple
2222
## Checklist
2323

2424
- [ ] I have performed a self-review of my own code
25-
- [ ] I have reviewed the title/description of this PR which will be used as the squashed PR commit message
25+
- [ ] I have reviewed the title & description of this PR which I will use as the squashed PR commit message
2626
- [ ] I have commented my code, particularly in hard-to-understand areas
2727
- [ ] I have added tests that prove my fix is effective or that my feature works
28+
- [ ] I have enabled auto-merge (optional)
2829

2930
## How to test
3031

.github/workflows/integrate-and-deploy.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,16 @@ jobs:
7474
-
7575
name: Unit Tests
7676
run: make unit-tests-ci
77-
# -
78-
# name: E2E Tests
79-
# run: |
80-
# docker compose -f docker-compose.yml up -d app-for-playwright
81-
# npx playwright install chromium
82-
# npx playwright test
77+
-
78+
name: Playwright E2E Tests
79+
run: make playwright-tests-ci
80+
-
81+
name: Upload Playwright test results
82+
if: always()
83+
uses: actions/upload-artifact@v2
84+
with:
85+
name: test-results
86+
path: test-results
8387
-
8488
name: Log in to Docker Hub
8589
uses: docker/login-action@v2

.github/workflows/pull-request.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ jobs:
6262
run: make playwright-tests-ci
6363

6464
-
65-
name: Upload test results
65+
name: Upload Playwright test results
6666
if: always()
6767
uses: actions/upload-artifact@v3
6868
with:
6969
name: test-results
70-
path: test-results
70+
path: test/e2e/test-results
7171

7272
check-code-formatting:
7373
runs-on: ubuntu-latest

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ test/php/\.phpunit\.result\.cache
1010
node_modules
1111
.DS_Store
1212
docker-scan-results.txt
13-
test-results/
14-
playwright-report/
13+
test-results
14+
test-storage-state
1515
*storageState.json

Makefile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ dev: start
1313
playwright-tests-ci:
1414
npm ci
1515
$(MAKE) playwright-app
16-
npx playwright install chromium && npx playwright test
16+
npx playwright install chromium && npx playwright test -c ./test/e2e/playwright.config.ts
1717

1818
.PHONY: playwright-tests
1919
playwright-tests:
2020
npm install
2121
$(MAKE) playwright-app
2222
docker compose up -d ui-builder
23-
npx playwright install chromium && npx playwright test $(params)
23+
npx playwright install chromium && npx playwright test -c ./test/e2e/playwright.config.ts $(params)
2424

2525
.PHONY: playwright-app
2626
playwright-app:
2727
# delete any cached session storage state files if the service isn't running
28-
docker compose ps app-for-playwright > /dev/null 2>&1 || rm -f *-storageState.json
28+
docker compose ps app-for-playwright > /dev/null 2>&1 || $(MAKE) clean-test
2929
docker compose up -d app-for-playwright
3030
# wait until the app-for-playwright service is serving up HTTP before continuing
3131
until curl localhost:3238 > /dev/null 2>&1; do sleep 1; done
@@ -83,9 +83,12 @@ clean:
8383
docker compose down
8484
docker system prune -f
8585

86+
clean-test:
87+
cd test/e2e && npx rimraf test-storage-state test-results
88+
8689
.PHONY: clean-powerwash
8790
clean-powerwash: clean
88-
npx rimraf *storageState.json test-results
91+
$(MAKE) clean-test
8992
docker system prune -f --volumes
9093
- docker rmi -f `docker images -q "lf-*"` sillsdev/web-languageforge:base-php
9194
docker builder prune -f

next-app/src/routes/projects/[project_code]/activities/+server.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,24 @@ import { sf } from '$lib/fetch/server'
44
export async function GET({ params: { project_code }, request: { headers } }) {
55
const cookie = headers.get('cookie')
66

7-
const activities = await get_activities({ project_code, cookie })
7+
await sf({ name: 'set_project', args: [ project_code ], cookie })
8+
9+
const activities = await get_activities({ cookie })
810

911
return json(activities)
1012
}
1113

1214
// src/Api/Model/Shared/Dto/ActivityListDto.php
1315
// src/Api/Model/Shared/Dto/ActivityListDto.php->ActivityListModel.__construct
14-
export async function get_activities({ project_code, cookie, start_date, end_date }) {
16+
export async function get_activities({ cookie, start_date, end_date }) {
1517
const args = {
16-
name: 'activity_list_dto_for_project',
18+
name: 'activity_list_dto_for_current_project',
1719
args: [
18-
project_code,
1920
{
2021
startDate: start_date,
2122
endDate: end_date,
2223
limit: start_date || end_date ? 50 : 0,
23-
}
24+
},
2425
],
2526
cookie,
2627
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"webpack:dev:watch": "webpack -w --config webpack-dev.config.js",
1616
"webpack:prd": "webpack --config webpack-prd.config.js",
1717
"compile-test-e2e": "tsc -p test/app",
18-
"test-e2e": "protractor test/app/protractorConf.js",
18+
"test-e2e": "npx playwright test -c ./test/e2e/playwright.config.ts",
1919
"prepare": "husky install"
2020
},
2121
"license": "MIT",

src/Api/Model/Shared/Dto/RightsHelper.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,6 @@ public function userCanAccessMethod($methodName)
210210
case "activity_list_dto_for_current_project":
211211
return $this->userHasSiteRight(Domain::PROJECTS + Operation::VIEW_OWN);
212212

213-
case "activity_list_dto_for_project":
214-
return $this->userHasSiteRight(Domain::PROJECTS + Operation::VIEW_OWN);
215-
216213
case "activity_list_dto_for_lexical_entry":
217214
return $this->userHasProjectRight(Domain::ENTRIES + Operation::VIEW);
218215

src/Api/Service/Sf.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -438,16 +438,6 @@ public function activity_list_dto_for_current_project($filterParams = [])
438438
return ActivityListDto::getActivityForOneProject($projectModel, $this->userId, $filterParams);
439439
}
440440

441-
public function activity_list_dto_for_project($projectCode, $filterParams = [])
442-
{
443-
$projectModel = ProjectModel::getByProjectCode($projectCode);
444-
$user = new UserModel($this->userId);
445-
if ($user->isMemberOfProject($projectModel->id->asString())) {
446-
return ActivityListDto::getActivityForOneProject($projectModel, $this->userId, $filterParams);
447-
}
448-
throw new UserUnauthorizedException("User $this->userId is not a member of project $projectCode");
449-
}
450-
451441
public function activity_list_dto_for_lexical_entry($entryId, $filterParams = [])
452442
{
453443
$projectModel = ProjectModel::getById($this->projectId);

src/angular-app/languageforge/lexicon/editor/editor-list.view.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@
109109
</div>
110110
</div>
111111
</div>
112-
<div class="row" data-ng-show="$ctrl.entries.length == 0 && $ctrl.lecRights.canEditProject() && $ctrl.lecFinishedLoading">
112+
<div class="row" data-ng-show="$ctrl.entries.length == 0 && $ctrl.lecFinishedLoading">
113113
<div class="col">
114114
<div class="lexiconItemListContainer" data-pui-when-scrolled="$ctrl.show.more()">
115115
<div class="text-center no-entries" id="noEntries">
116116
<h4>Looks like there are no entries yet.</h4>
117-
<button class="btn btn-primary" data-ng-click="$ctrl.navigateToLiftImport()"
117+
<button class="btn btn-primary" data-ng-if="$ctrl.lecRights.canEditEntry()" data-ng-click="$ctrl.navigateToLiftImport()"
118118
data-ng-hide="$ctrl.projectSettings.hasSendReceive">
119119
<i class="fa fa-upload"></i> Import entries from LIFT</button>
120120
<button class="btn btn-primary" data-ng-click="$ctrl.syncProject()" data-ng-show="$ctrl.projectSettings.hasSendReceive">

src/angular-app/languageforge/lexicon/editor/editor.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ export class LexiconEditorController implements angular.IController {
520520

521521
deleteEntry = (entry: LexEntry): void => {
522522
const deleteMsg = 'Are you sure you want to delete the entry <b>\'' +
523-
LexiconUtilityService.getLexeme(this.lecConfig, this.lecConfig.entry, entry) + '\'</b>';
523+
LexiconUtilityService.getLexeme(this.lecConfig, this.lecConfig.entry, entry) + '\'</b>?';
524524
this.modal.showModalSimple('Delete Entry', deleteMsg, 'Cancel', 'Delete Entry').then(() => {
525525
let iShowList = this.editorService.getIndexInList(entry.id, this.visibleEntries);
526526
this.editorService.removeEntryFromLists(entry.id);

src/angular-app/languageforge/lexicon/editor/field/dc-audio.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class FieldAudioController implements angular.IController {
7171
deleteAudio(): void {
7272
if (this.hasAudio()) {
7373
const deleteMsg = 'Are you sure you want to delete the audio <b>\'' +
74-
FieldAudioController.originalFileName(this.dcFilename) + '\'</b>';
74+
FieldAudioController.originalFileName(this.dcFilename) + '\'</b>?';
7575
this.modalService.showModalSimple('Delete Audio', deleteMsg, 'Cancel', 'Delete Audio')
7676
.then(() => {
7777
this.lexProjectService.removeMediaFile('audio', this.dcFilename, result => {

src/angular-app/languageforge/lexicon/editor/field/dc-entry.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<div data-ng-if="$ctrl.control.rights.canDeleteEntry() && $ctrl.isAtEditorEntry()" class="d-flex align-items-center justify-content-between">
55
Entry
66

7-
<a href data-ng-click="$ctrl.deleteEntry()" class="text-danger" role="button">
7+
<a href data-ng-click="$ctrl.deleteEntry()" class="text-danger" role="button" title="Delete this entry">
88
<span class="fa fa-trash"></span>
99
</a>
1010
</div>

src/angular-app/languageforge/lexicon/editor/field/dc-entry.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ export class FieldEntryController implements angular.IController {
5252
}
5353

5454
deleteSense = (index: number): void => {
55-
const deletemsg = 'Are you sure you want to delete the meaning <b>\' ' +
55+
const deletemsg = 'Are you sure you want to delete the meaning <b>\'' +
5656
LexiconUtilityService.getMeaning(this.control.config, this.config.fields.senses as LexConfigFieldList,
57-
this.model.senses[index]) + ' \'</b>';
57+
this.model.senses[index]) + '\'</b>?';
5858
this.modal.showModalSimple('Delete Meaning', deletemsg, 'Cancel', 'Delete Meaning')
5959
.then(() => {
6060
// Adding or removing senses makes for a non-delta update, so save a possible delta update first

src/angular-app/languageforge/lexicon/editor/field/dc-example.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
<span class="flex-grow"></span>
77

88
<div class="mr-4">
9-
<a href data-ng-show="$ctrl.index > 0" data-ng-click="$ctrl.move($ctrl.index, -1)" class="mr-1" role="button">
9+
<a href data-ng-show="$ctrl.index > 0" data-ng-click="$ctrl.move($ctrl.index, -1)" class="mr-1" role="button" title="Move this example up">
1010
<span class="fa fa-arrow-up"></span>
1111
</a>
12-
<a href data-ng-show="$ctrl.index+1 < $ctrl.numExamples()" data-ng-click="$ctrl.move($ctrl.index, 1)" class="mr-1" role="button">
12+
<a href data-ng-show="$ctrl.index+1 < $ctrl.numExamples()" data-ng-click="$ctrl.move($ctrl.index, 1)" class="mr-1" role="button" title="Move this example down">
1313
<span class="fa fa-arrow-down"></span>
1414
</a>
1515
</div>
1616

17-
<a href data-ng-click="$ctrl.remove($ctrl.index)" class="text-danger" role="button">
17+
<a href data-ng-click="$ctrl.remove($ctrl.index)" class="text-danger" role="button" title="Delete this example">
1818
<span class="fa fa-trash"></span>
1919
</a>
2020
</div>

src/angular-app/languageforge/lexicon/editor/field/dc-picture.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class FieldPictureController implements angular.IController {
7272
const fileName: string = this.pictures[index].fileName;
7373
if (fileName) {
7474
const deleteMsg: string = 'Are you sure you want to delete the picture <b>\'' +
75-
FieldPictureController.originalFileName(fileName) + '\'</b>';
75+
FieldPictureController.originalFileName(fileName) + '\'</b>?';
7676
this.modalService.showModalSimple('Delete Picture', deleteMsg, 'Cancel', 'Delete Picture').then(() => {
7777
this.pictures.splice(index, 1);
7878
this.lexProjectService.removeMediaFile('sense-image', fileName, result => {

src/angular-app/languageforge/lexicon/editor/field/dc-sense.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
<span class="flex-grow"></span>
77

88
<div class="mr-4">
9-
<a href data-ng-show="$ctrl.index > 0" data-ng-click="$ctrl.move($ctrl.index, -1)" class="mr-1" role="button">
9+
<a href data-ng-show="$ctrl.index > 0" data-ng-click="$ctrl.move($ctrl.index, -1)" class="mr-1" role="button" title="Move this meaning up">
1010
<span class="fa fa-arrow-up"></span>
1111
</a>
12-
<a href data-ng-show="$ctrl.index+1 < $ctrl.numSenses()" data-ng-click="$ctrl.move($ctrl.index, 1)" class="mr-1" role="button">
12+
<a href data-ng-show="$ctrl.index+1 < $ctrl.numSenses()" data-ng-click="$ctrl.move($ctrl.index, 1)" class="mr-1" role="button" title="Move this meaning down">
1313
<span class="fa fa-arrow-down"></span>
1414
</a>
1515
</div>
1616

17-
<a href data-ng-click="$ctrl.remove($ctrl.index)" class="text-danger" role="button">
17+
<a href data-ng-click="$ctrl.remove($ctrl.index)" class="text-danger" role="button" title="Delete this meaning">
1818
<span class="fa fa-trash"></span>
1919
</a>
2020
</div>

src/angular-app/languageforge/lexicon/editor/field/dc-sense.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ export class FieldSenseController implements angular.IController {
6767

6868
// noinspection JSUnusedGlobalSymbols
6969
deleteExample = (index: number): void => {
70-
const deletemsg = 'Are you sure you want to delete the example <b>\' ' +
70+
const deletemsg = 'Are you sure you want to delete the example <b>\'' +
7171
LexiconUtilityService.getExample(this.control.config, this.config.fields.examples as LexConfigFieldList,
7272
this.model.examples[index], 'sentence')
73-
+ ' \'</b>';
73+
+ '\'</b>?';
7474
this.modal.showModalSimple('Delete Example', deletemsg, 'Cancel', 'Delete Example')
7575
.then(() => {
7676
// Adding or removing examples makes for a non-delta update, so save a possible delta update first

src/angular-app/languageforge/lexicon/settings/configuration/configuration-option-lists.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export class OptionListConfigurationController implements angular.IController {
1818
return null;
1919
}
2020

21-
return this.olcOptionListsDirty[this.currentListIndex].items;
21+
return this.olcOptionListsDirty[this.currentListIndex]?.items;
2222
},
2323
(newVal: LexOptionListItem[], oldVal: LexOptionListItem[]) => {
2424
if (newVal != null && newVal !== oldVal) {

src/angular-app/languageforge/lexicon/settings/configuration/field-unified-view.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,9 +422,9 @@ export class ConfigurationFieldUnifiedViewModel {
422422
const userView: LexUserViewConfig = config.userViews[userId];
423423
if (userView != null) {
424424
if (userView.inputSystems && userView.inputSystems.length) {
425-
inputSystemSettings.groups[groupIndex].show = userView.inputSystems.includes(tag);
425+
inputSystemSettings.groups[groupIndex] = {show: userView.inputSystems.includes(tag)};
426426
} else {
427-
inputSystemSettings.groups[groupIndex].show = true;
427+
inputSystemSettings.groups[groupIndex] = {show: true};
428428
}
429429
}
430430
}

src/angular-app/languageforge/lexicon/settings/project-settings.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</div>
2828
<div class="form-group row">
2929
<label class="col-form-label col-12">Project Owner</label>
30-
<div data-testid="e2e-test-project-owner" class="controls col-12 notranslate">
30+
<div class="controls col-12 notranslate">
3131
{{$ctrl.project.ownerRef.username}}
3232
</div>
3333
</div>

test/app/languageforge/lexicon/settings/lexicon-project-settings.e2e-spec.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

test/app/testConstants.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
"observerName": "Test ObserverUser",
3737
"observerPassword": "normaluser5",
3838
"observerEmail": "[email protected]",
39+
"writableUsername": "test_runner_writable_user",
40+
"writableName": "Test WritableUser",
41+
"writablePassword": "writableuser5",
42+
"writableEmail": "[email protected]",
3943
"unusedUsername": "test_runner_unused_user",
4044
"unusedName": "Test UnusedUser",
4145
"unusedEmail": "[email protected]",

test/e2e/pages/base-page.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export abstract class BasePage {
1919

2020
async waitForPage(): Promise<void> {
2121
await Promise.all([
22-
this.page.waitForNavigation({ url: new RegExp(`${this.url}(#|$)`) }),
22+
this.page.waitForNavigation({url: new RegExp(`${this.url}(#|$)`)}),
2323
this.waitFor?.waitFor(),
2424
]);
2525
}

test/e2e/pages/editor.page.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Project } from '../utils/types';
33
import { BasePage, GotoOptions } from './base-page';
44
import { ConfigurationPage } from './configuration.page';
55
import { EntriesListPage } from './entries-list.page';
6+
import { ProjectSettingsPage } from './project-settings.page';
67

78
export interface EditorGotoOptions extends GotoOptions {
89
entryId?: string;
@@ -67,7 +68,7 @@ export class EditorPage extends BasePage {
6768
readonly addPictureButtonSelector = 'a >> text=Add Picture';
6869

6970
constructor(page: Page, readonly project: Project) {
70-
super(page, `/app/lexicon/${project.id}/`, page.locator('.words-container-title'));
71+
super(page, `/app/lexicon/${project.id}/`, page.locator('.words-container-title, .no-entries'));
7172
}
7273

7374
async goto(options?: EditorGotoOptions): Promise<void> {
@@ -83,11 +84,16 @@ export class EditorPage extends BasePage {
8384
await expect(this.page.locator('.page-name >> text=' + this.project.name)).toBeVisible();
8485
}
8586

86-
async navigateToSettings() {
87+
async navigateToSettings(): Promise<ProjectSettingsPage> {
8788
await expect(this.settingsMenuLink).toBeVisible();
8889
await this.settingsMenuLink.click();
8990
await expect(this.projectSettingsLink).toBeVisible();
90-
await this.projectSettingsLink.click();
91+
const projectSettingsPage = new ProjectSettingsPage(this.page, this.project);
92+
await Promise.all([
93+
this.projectSettingsLink.click(),
94+
projectSettingsPage.waitForPage(),
95+
]);
96+
return projectSettingsPage;
9197
}
9298

9399
async navigateToEntriesList() {

0 commit comments

Comments
 (0)