Skip to content

Commit efe4d38

Browse files
authored
SF-3652 Don't allow new activations for translation suggestions (#3588)
1 parent ef9155e commit efe4d38

File tree

3 files changed

+67
-67
lines changed

3 files changed

+67
-67
lines changed

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.html

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,25 @@ <h1>{{ t("settings") }}</h1>
6868
}
6969
}
7070
</div>
71-
<div class="tool-setting">
72-
@if (isBasedOnProjectSet) {
73-
<div class="tool-setting-field checkbox-field">
74-
<mat-checkbox formControlName="translationSuggestionsEnabled" id="checkbox-translation-suggestions">
75-
{{ t("translation_suggestions") }}
76-
</mat-checkbox>
77-
<app-info [text]="t('translations_will_be_suggested')"></app-info>
78-
<app-write-status
79-
[state]="getControlState('translationSuggestionsEnabled')"
80-
[formGroup]="form"
81-
id="translation-suggestions-status"
82-
></app-write-status>
83-
</div>
84-
} @else {
85-
<mat-hint>{{ t("translation_suggestions_require_source_text") }}</mat-hint>
86-
}
87-
</div>
71+
@if (isTranslationSuggestionsEnabled || featureFlags.showDeveloperTools.enabled) {
72+
<div class="tool-setting">
73+
@if (isBasedOnProjectSet) {
74+
<div class="tool-setting-field checkbox-field">
75+
<mat-checkbox formControlName="translationSuggestionsEnabled" id="checkbox-translation-suggestions">
76+
{{ t("translation_suggestions") }}
77+
</mat-checkbox>
78+
<app-info [text]="t('translations_will_be_suggested')"></app-info>
79+
<app-write-status
80+
[state]="getControlState('translationSuggestionsEnabled')"
81+
[formGroup]="form"
82+
id="translation-suggestions-status"
83+
></app-write-status>
84+
</div>
85+
} @else {
86+
<mat-hint>{{ t("translation_suggestions_require_source_text") }}</mat-hint>
87+
}
88+
</div>
89+
}
8890
<div class="tool-setting">
8991
<div class="tool-setting-field checkbox-field">
9092
<mat-checkbox formControlName="biblicalTermsEnabled" id="checkbox-biblical-terms">

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.spec.ts

Lines changed: 46 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { createTestTextAudio } from 'realtime-server/lib/esm/scriptureforge/mode
2020
import { of } from 'rxjs';
2121
import { anything, capture, deepEqual, instance, mock, verify, when } from 'ts-mockito';
2222
import { AuthService } from 'xforge-common/auth.service';
23+
import { createTestFeatureFlag, FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service';
2324
import { NoticeService } from 'xforge-common/notice.service';
2425
import { OnlineStatusService } from 'xforge-common/online-status.service';
2526
import { QueryParameters } from 'xforge-common/query-parameters';
@@ -44,6 +45,7 @@ import { SettingsComponent } from './settings.component';
4445

4546
const mockedActivatedRoute = mock(ActivatedRoute);
4647
const mockedAuthService = mock(AuthService);
48+
const mockedFeatureFlagService = mock(FeatureFlagService);
4749
const mockedNoticeService = mock(NoticeService);
4850
const mockedParatextService = mock(ParatextService);
4951
const mockedSFProjectService = mock(SFProjectService);
@@ -73,6 +75,7 @@ describe('SettingsComponent', () => {
7375
provideTestOnlineStatus(),
7476
{ provide: ActivatedRoute, useMock: mockedActivatedRoute },
7577
{ provide: AuthService, useMock: mockedAuthService },
78+
{ provide: FeatureFlagService, useMock: mockedFeatureFlagService },
7679
{ provide: NoticeService, useMock: mockedNoticeService },
7780
{ provide: ParatextService, useMock: mockedParatextService },
7881
{ provide: SFProjectService, useMock: mockedSFProjectService },
@@ -98,7 +101,7 @@ describe('SettingsComponent', () => {
98101

99102
it('changing state of top-level setting results in status icon', fakeAsync(() => {
100103
const env = new TestEnvironment();
101-
env.setupProject();
104+
env.setupProject({ biblicalTermsConfig: { biblicalTermsEnabled: true } });
102105
env.wait();
103106
expect(env.statusDone(env.checkingStatus)).toBeNull();
104107
expect(env.inputElement(env.checkingCheckbox).checked).toBe(false);
@@ -107,12 +110,12 @@ describe('SettingsComponent', () => {
107110
env.fixture.detectChanges();
108111
expect(env.statusDone(env.checkingStatus)).not.toBeNull();
109112

110-
expect(env.statusDone(env.translationSuggestionsStatus)).toBeNull();
111-
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(true);
112-
env.clickElement(env.inputElement(env.translationSuggestionsCheckbox));
113+
expect(env.statusDone(env.biblicalTermsStatus)).toBeNull();
114+
expect(env.inputElement(env.biblicalTermsCheckbox).checked).toBe(true);
115+
env.clickElement(env.inputElement(env.biblicalTermsCheckbox));
113116
tick();
114117
env.fixture.detectChanges();
115-
expect(env.statusDone(env.translationSuggestionsStatus)).not.toBeNull();
118+
expect(env.statusDone(env.biblicalTermsStatus)).not.toBeNull();
116119
}));
117120

118121
it('error on data submit shows error icon', fakeAsync(() => {
@@ -157,7 +160,7 @@ describe('SettingsComponent', () => {
157160
env.wait();
158161

159162
expect(env.component.form.disabled).toBe(false);
160-
expect(env.inputElement(env.translationSuggestionsCheckbox).disabled).toBe(false);
163+
expect(env.inputElement(env.biblicalTermsCheckbox).disabled).toBe(false);
161164
expect(env.basedOnSelectErrorMessage.textContent).toContain('error fetching projects and resources');
162165
expect(env.basedOnSelectComponent.isDisabled).toBe(true);
163166
}));
@@ -172,7 +175,7 @@ describe('SettingsComponent', () => {
172175
expect(env.component.form.disabled).toBe(false);
173176
expect(env.basedOnSelectErrorMessage.textContent).toContain('error fetching projects.');
174177
expect(env.basedOnSelectComponent.isDisabled).toBe(false);
175-
expect(env.inputElement(env.translationSuggestionsCheckbox).disabled).toBe(false);
178+
expect(env.inputElement(env.biblicalTermsCheckbox).disabled).toBe(false);
176179
}));
177180

178181
it('enables form even when resources fail to load', fakeAsync(() => {
@@ -185,13 +188,13 @@ describe('SettingsComponent', () => {
185188
expect(env.component.form.disabled).toBe(false);
186189
expect(env.basedOnSelectErrorMessage.textContent).toContain('error fetching the Digital Bible Library resources');
187190
expect(env.basedOnSelectComponent.isDisabled).toBe(false);
188-
expect(env.inputElement(env.translationSuggestionsCheckbox).disabled).toBe(false);
191+
expect(env.inputElement(env.biblicalTermsCheckbox).disabled).toBe(false);
189192
}));
190193

191194
describe('Translation Suggestions options', () => {
192195
it('should see login button when Paratext account not connected', fakeAsync(() => {
193196
const env = new TestEnvironment();
194-
env.setupProject();
197+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
195198
when(mockedParatextService.getParatextUsername()).thenReturn(of(undefined));
196199
env.wait();
197200
expect(env.loginButton).not.toBeNull();
@@ -214,7 +217,7 @@ describe('SettingsComponent', () => {
214217

215218
it('should hide Translation Suggestions when Based On is not set', fakeAsync(() => {
216219
const env = new TestEnvironment();
217-
env.setupProject();
220+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
218221
env.wait();
219222
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(true);
220223
expect(env.basedOnSelect).not.toBeNull();
@@ -226,17 +229,10 @@ describe('SettingsComponent', () => {
226229
expect(env.basedOnSelectValue).toEqual('');
227230
}));
228231

229-
it('should show Translation Suggestions when Based On is set', fakeAsync(() => {
232+
it('should show Translation Suggestions when Based On is set and the feature flag is enabled', fakeAsync(() => {
230233
const env = new TestEnvironment();
231-
env.setupProject(
232-
{
233-
translateConfig: {
234-
translationSuggestionsEnabled: false,
235-
source: undefined
236-
}
237-
},
238-
true
239-
);
234+
when(mockedFeatureFlagService.showDeveloperTools).thenReturn(createTestFeatureFlag(true));
235+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: false, source: undefined } }, true);
240236
env.wait();
241237
expect(env.translationSuggestionsCheckbox).toBeNull();
242238
expect(env.basedOnSelectValue).toEqual('');
@@ -247,9 +243,23 @@ describe('SettingsComponent', () => {
247243
expect(env.basedOnSelectValue).toEqual('ParatextP1');
248244
}));
249245

246+
it('should not show Translation Suggestions when Based On is set and the feature flag is not enabled', fakeAsync(() => {
247+
const env = new TestEnvironment();
248+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: false, source: undefined } }, true);
249+
env.wait();
250+
expect(env.translationSuggestionsCheckbox).toBeNull();
251+
expect(env.basedOnSelectValue).toEqual('');
252+
253+
env.setBasedOnValue('paratextId01');
254+
255+
expect(env.translationSuggestionsCheckbox).toBeNull();
256+
expect(env.basedOnSelectValue).toEqual('ParatextP1');
257+
}));
258+
250259
it('should retain Based On value when Translation Suggestions is disabled', fakeAsync(() => {
251260
const env = new TestEnvironment();
252-
env.setupProject();
261+
when(mockedFeatureFlagService.showDeveloperTools).thenReturn(createTestFeatureFlag(true));
262+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
253263
env.wait();
254264
env.clickElement(env.inputElement(env.checkingCheckbox));
255265
expect(env.inputElement(env.checkingCheckbox).checked).toBe(true);
@@ -273,7 +283,7 @@ describe('SettingsComponent', () => {
273283

274284
it('should change Based On select value', fakeAsync(() => {
275285
const env = new TestEnvironment();
276-
env.setupProject();
286+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
277287
env.wait();
278288
env.wait();
279289
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(true);
@@ -289,7 +299,7 @@ describe('SettingsComponent', () => {
289299

290300
it('should display Based On project even if user is not a member', fakeAsync(() => {
291301
const env = new TestEnvironment();
292-
env.setupProject();
302+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
293303
when(mockedParatextService.getProjects()).thenResolve([
294304
{
295305
paratextId: 'paratextId02',
@@ -315,7 +325,7 @@ describe('SettingsComponent', () => {
315325

316326
it('should display projects then resources', fakeAsync(() => {
317327
const env = new TestEnvironment();
318-
env.setupProject();
328+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
319329
env.wait();
320330
env.wait();
321331
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(true);
@@ -327,14 +337,8 @@ describe('SettingsComponent', () => {
327337

328338
it('Translation Suggestions should remain unchanged when Based On is changed', fakeAsync(() => {
329339
const env = new TestEnvironment();
330-
env.setupProject(
331-
{
332-
translateConfig: {
333-
translationSuggestionsEnabled: false
334-
}
335-
},
336-
true
337-
);
340+
when(mockedFeatureFlagService.showDeveloperTools).thenReturn(createTestFeatureFlag(true));
341+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: false } }, true);
338342
env.wait();
339343
expect(env.translationSuggestionsCheckbox).toBeNull();
340344
expect(env.basedOnSelectValue).toEqual('');
@@ -362,20 +366,8 @@ describe('SettingsComponent', () => {
362366

363367
it('should save Translation Suggestions only if Based On is set', fakeAsync(() => {
364368
const env = new TestEnvironment();
365-
env.setupProject({
366-
translateConfig: {
367-
translationSuggestionsEnabled: false,
368-
source: {
369-
paratextId: 'paratextId01',
370-
projectRef: 'paratext01',
371-
name: 'ParatextP1',
372-
shortName: 'PT1',
373-
writingSystem: {
374-
tag: 'qaa'
375-
}
376-
}
377-
}
378-
});
369+
when(mockedFeatureFlagService.showDeveloperTools).thenReturn(createTestFeatureFlag(true));
370+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: false } });
379371
env.wait();
380372
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(false);
381373

@@ -391,9 +383,9 @@ describe('SettingsComponent', () => {
391383
it('should hide options when Checking is disabled', fakeAsync(() => {
392384
const env = new TestEnvironment();
393385
env.makeProjectHaveTextAudio();
394-
env.setupProject();
386+
env.setupProject({ biblicalTermsConfig: { biblicalTermsEnabled: true } });
395387
env.wait();
396-
expect(env.inputElement(env.translationSuggestionsCheckbox).checked).toBe(true);
388+
expect(env.inputElement(env.biblicalTermsCheckbox).checked).toBe(true);
397389
expect(env.inputElement(env.checkingCheckbox).checked).toBe(false);
398390
expect(env.seeOthersResponsesCheckbox).toBeNull();
399391
expect(env.communityCheckersShareCheckbox).toBeNull();
@@ -702,7 +694,7 @@ describe('SettingsComponent', () => {
702694

703695
it('should hide/disabled settings while loading', fakeAsync(() => {
704696
const env = new TestEnvironment();
705-
env.setupProject();
697+
env.setupProject({ translateConfig: { translationSuggestionsEnabled: true } });
706698
env.fixture.detectChanges();
707699
expect(env.translationSuggestionsCheckbox).toBeNull();
708700
expect(env.basedOnSelect).toBeNull();
@@ -773,6 +765,7 @@ class TestEnvironment {
773765
constructor(hasConnection: boolean = true, isSource: boolean = false) {
774766
when(mockedActivatedRoute.params).thenReturn(of({ projectId: 'project01' }));
775767
when(mockedAuthService.currentUserRoles).thenReturn([]);
768+
when(mockedFeatureFlagService.showDeveloperTools).thenReturn(createTestFeatureFlag(false));
776769
when(mockedSFProjectService.onlineIsSourceProject('project01')).thenResolve(isSource);
777770
when(mockedSFProjectService.onlineDelete(anything())).thenResolve();
778771
when(mockedSFProjectService.onlineUpdateSettings('project01', anything())).thenResolve();
@@ -925,6 +918,10 @@ class TestEnvironment {
925918
return this.fixture.debugElement.query(By.css('#checkbox-biblical-terms'));
926919
}
927920

921+
get biblicalTermsStatus(): DebugElement {
922+
return this.fixture.debugElement.query(By.css('#biblical-terms-status'));
923+
}
924+
928925
set onlineStatus(hasConnection: boolean) {
929926
this.testOnlineStatusService.setIsOnline(hasConnection);
930927
tick();
@@ -1032,7 +1029,6 @@ class TestEnvironment {
10321029

10331030
testProject: SFProject = createTestProject({
10341031
translateConfig: {
1035-
translationSuggestionsEnabled: true,
10361032
source: {
10371033
paratextId: 'paratextId01',
10381034
projectRef: 'paratext01',

src/SIL.XForge.Scripture/ClientApp/src/app/settings/settings.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { AuthService } from 'xforge-common/auth.service';
2323
import { DataLoadingComponent } from 'xforge-common/data-loading-component';
2424
import { DialogService } from 'xforge-common/dialog.service';
2525
import { ExternalUrlService } from 'xforge-common/external-url.service';
26+
import { FeatureFlagService } from 'xforge-common/feature-flags/feature-flag.service';
2627
import { I18nService, TextAroundTemplate } from 'xforge-common/i18n.service';
2728
import { ElementState } from 'xforge-common/models/element-state';
2829
import { UserDoc } from 'xforge-common/models/user-doc';
@@ -127,6 +128,7 @@ export class SettingsComponent extends DataLoadingComponent implements OnInit {
127128

128129
constructor(
129130
private readonly route: ActivatedRoute,
131+
protected readonly featureFlags: FeatureFlagService,
130132
noticeService: NoticeService,
131133
private readonly paratextService: ParatextService,
132134
private readonly projectService: SFProjectService,

0 commit comments

Comments
 (0)