From 4832f461e9434ea5104b260087fdf3c74451fa54 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 22 Dec 2023 08:43:39 -0500 Subject: [PATCH 1/8] Adding new async publish feature for single and bulk. --- .../multiple-draft-generator.component.css | 9 + .../multiple-draft-generator.component.html | 82 +++++-- .../multiple-draft-generator.component.ts | 204 ++++++++++++++++-- .../resource/resource.component.html | 2 +- .../translation/translation.component.html | 10 +- .../translation/translation.component.ts | 92 +++++--- src/app/models/message.ts | 5 + src/app/service/draft.service.ts | 24 ++- 8 files changed, 358 insertions(+), 70 deletions(-) create mode 100644 src/app/components/multiple-draft-generator/multiple-draft-generator.component.css create mode 100644 src/app/models/message.ts diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css new file mode 100644 index 00000000..9f074a8e --- /dev/null +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css @@ -0,0 +1,9 @@ +.toggle-drafts { + width: 100%; + display: flex; + margin-bottom: 15px; +} + +.toggle-drafts label.btn { + flex: 0 1 50%; +} diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html index c4fc4aa5..c2bb4493 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html @@ -1,10 +1,44 @@ diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts index 1947d11e..2c7d4e9c 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts @@ -1,20 +1,44 @@ -import { Component } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { Resource } from '../../models/resource'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { DraftService } from '../../service/draft.service'; +import { ResourceService } from '../../service/resource/resource.service'; +import { LanguageService } from '../../service/language.service'; import { Translation } from '../../models/translation'; +import { MessageType } from '../../models/message'; + +enum LanguageTypeEnum { + draft = 'draft', + publish = 'publish', +} + +interface PromisePayload { + success: boolean; + value?: any; + error?: string; + type: LanguageTypeEnum; +} +interface APICall { + type: LanguageTypeEnum; + translation: Translation; +} + +type LanguagesType = 'published' | 'drafts'; @Component({ selector: 'admin-multiple-draft-generator', templateUrl: './multiple-draft-generator.component.html', + styleUrls: ['./multiple-draft-generator.component.css'], }) -export class MultipleDraftGeneratorComponent { +export class MultipleDraftGeneratorComponent implements OnDestroy { resource: Resource; translations: Translation[]; - + languageType: LanguagesType = 'published'; confirmMessage: string; - saving: boolean; - errorMessage: string; + errorMessage: string[]; + alertMessage: string; + sucessfulMessage: string; + checkToEnsureDraftIsPublished: number; readonly baseConfirmMessage = 'Are you sure you want to generate a draft for these languages:'; @@ -22,8 +46,27 @@ export class MultipleDraftGeneratorComponent { constructor( private ngbActiveModal: NgbActiveModal, private draftService: DraftService, + private resourceService: ResourceService, + private languageService: LanguageService, ) {} + ngOnDestroy(): void { + clearInterval(this.checkToEnsureDraftIsPublished); + } + + renderMessage(type: MessageType, text: string, time?: number) { + if (type === MessageType.error) { + this.errorMessage = [text]; + return; + } + this[`${type}Message`] = text; + if (time) { + setTimeout(() => { + this[`${type}Message`] = ''; + }, time); + } + } + showConfirmAlert(): void { this.translations = this.resource['latest-drafts-translations'].filter( (translation) => translation.generateDraft, @@ -39,21 +82,146 @@ export class MultipleDraftGeneratorComponent { this.confirmMessage = `${this.baseConfirmMessage} ${message}?`; } - generateDrafts(): void { - this.saving = true; - this.errorMessage = null; + async publishOrCreateDrafts(): Promise { + this.confirmMessage = null; + this.errorMessage = []; + const promises: APICall[] = []; + + // Define what promises we will call + this.translations.forEach((translation) => { + if (translation['is-published'] && this.languageType === 'published') { + promises.push({ + type: LanguageTypeEnum.draft, + translation, + }); + } else if ( + !translation['is-published'] && + this.languageType === 'drafts' + ) { + promises.push({ + type: LanguageTypeEnum.publish, + translation, + }); + } + }); + + // Call promises + if (promises.length) { + if (this.languageType === 'published') { + this.renderMessage(MessageType.alert, 'Creating drafts...'); + } else { + this.renderMessage(MessageType.success, 'Publishing drafts...'); + } - this.translations.forEach((translation, index) => { - this.draftService - .createDraft(translation) - .then(() => { - if (index === this.translations.length - 1) { - this.ngbActiveModal.close(); + const results: PromisePayload[] = await Promise.all( + promises.map(({ type, translation }) => { + if (type === LanguageTypeEnum.draft) { + return this.draftService + .createDraft(translation) + .then((value) => ({ + success: true, + type, + value, + })) + .catch((error) => ({ + success: false, + type, + error, + })); + } else { + return this.draftService + .publishDraft(this.resource, translation) + .then((value) => ({ + success: true, + type, + value, + })) + .catch((error) => ({ + success: false, + type, + error, + })); } - }) - .catch((message) => { - this.saving = false; - this.errorMessage = message; + }), + ); + + // Determine results + const invalidResults = results.filter((result) => !result.success); + if (invalidResults.length) { + invalidResults.forEach((invalidResult) => { + this.errorMessage = [...this.errorMessage, invalidResult.error]; + }); + } else { + if (this.languageType === 'published') { + this.renderMessage(MessageType.alert, ''); + this.renderMessage( + MessageType.success, + 'Drafts created. Ready for you to publish.', + ); + // Update languages + this.resourceService + .getResources('latest-drafts-translations') + .then((resources) => { + const resource = resources.find((r) => r.id === this.resource.id); + this.setResourceAndLoadTranslations(resource); + }); + setTimeout(() => { + this.renderMessage(MessageType.success, ''); + }, 5000); + } else { + const publishingErrors = results.filter( + (result) => result.value[0]['publishing-errors'], + ); + if (publishingErrors.length) { + publishingErrors.forEach((publishingError) => { + this.errorMessage = [...this.errorMessage, publishingError.error]; + }); + } + this.checkToEnsureDraftIsPublished = window.setInterval(() => { + this.isDraftPublished(); + }, 5000); + } + } + } + } + + isDraftPublished() { + this.resourceService + .getResources('latest-drafts-translations') + .then((resources) => { + const resource = resources.find((r) => r.id === this.resource.id); + let numberPubblished = 0; + this.translations.forEach((translation) => { + const updatedTranslation = resource[ + 'latest-drafts-translations' + ].find((t) => t.id === translation.id); + if (updatedTranslation) { + numberPubblished++; + } + }); + + if (numberPubblished === this.translations.length) { + clearInterval(this.checkToEnsureDraftIsPublished); + this.renderMessage( + MessageType.success, + 'Drafts are successfully published.', + ); + this.setResourceAndLoadTranslations(resource); + } + }) + .catch((err) => { + console.log('ERROR', err); + }); + } + + private setResourceAndLoadTranslations(resource: Resource): void { + this.resource = resource; + this.resource['latest-drafts-translations'].forEach((translation) => { + this.languageService + .getLanguage(translation.language.id, 'custom_pages,custom_tips') + .then((language) => { + translation.language = language; + translation.is_published = translation['is-published']; }); }); } diff --git a/src/app/components/resource/resource.component.html b/src/app/components/resource/resource.component.html index 1f327609..eb951410 100644 --- a/src/app/components/resource/resource.component.html +++ b/src/app/components/resource/resource.component.html @@ -28,7 +28,7 @@ class="btn btn-secondary" *ngIf="!isMetaTool()" > - Generate multiple drafts + Bulk Actions diff --git a/src/app/components/translation/translation.component.html b/src/app/components/translation/translation.component.html index 34bcdb90..3268f71f 100644 --- a/src/app/components/translation/translation.component.html +++ b/src/app/components/translation/translation.component.html @@ -52,7 +52,7 @@

- + diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts index b835168d..44a1d376 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts @@ -7,6 +7,8 @@ import { import { MultipleDraftGeneratorComponent } from './multiple-draft-generator.component'; import { FormsModule } from '@angular/forms'; import { DraftService } from '../../service/draft.service'; +import { LanguageService } from '../../service/language.service'; +import { ResourceService } from '../../service/resource/resource.service'; import { Resource } from '../../models/resource'; import { Translation } from '../../models/translation'; import { By } from '@angular/platform-browser'; @@ -29,6 +31,7 @@ describe('MultipleDraftGeneratorComponent', () => { const t = new Translation(); t.language = l; t.is_published = isPublished; + t['is-published'] = isPublished; t.generateDraft = generateDraft; return t; }; @@ -37,7 +40,12 @@ describe('MultipleDraftGeneratorComponent', () => { TestBed.configureTestingModule({ declarations: [MultipleDraftGeneratorComponent], imports: [NgbModule.forRoot(), FormsModule], - providers: [{ provide: DraftService }, { provide: NgbActiveModal }], + providers: [ + { provide: DraftService }, + { provide: NgbActiveModal }, + { provide: ResourceService }, + { provide: LanguageService }, + ], }).compileComponents(); fixture = TestBed.createComponent(MultipleDraftGeneratorComponent); @@ -53,6 +61,7 @@ describe('MultipleDraftGeneratorComponent', () => { const r = new Resource(); r['latest-drafts-translations'] = translations; comp.resource = r; + comp.languageType = 'published'; fixture.detectChanges(); }); diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts index 2c7d4e9c..381a2db5 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts @@ -14,9 +14,9 @@ enum LanguageTypeEnum { interface PromisePayload { success: boolean; - value?: any; - error?: string; type: LanguageTypeEnum; + value?: Translation; + error?: string; } interface APICall { type: LanguageTypeEnum; @@ -39,6 +39,7 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { alertMessage: string; sucessfulMessage: string; checkToEnsureDraftIsPublished: number; + disableButtons: boolean; readonly baseConfirmMessage = 'Are you sure you want to generate a draft for these languages:'; @@ -86,6 +87,7 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { this.confirmMessage = null; this.errorMessage = []; const promises: APICall[] = []; + this.disableButtons = true; // Define what promises we will call this.translations.forEach((translation) => { @@ -118,29 +120,40 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { if (type === LanguageTypeEnum.draft) { return this.draftService .createDraft(translation) - .then((value) => ({ - success: true, - type, - value, - })) - .catch((error) => ({ - success: false, - type, - error, - })); + .then( + () => + ({ + success: true, + type, + } as PromisePayload), + ) + .catch( + (error) => + ({ + success: false, + type, + error, + } as PromisePayload), + ); } else { return this.draftService .publishDraft(this.resource, translation) - .then((value) => ({ - success: true, - type, - value, - })) - .catch((error) => ({ - success: false, - type, - error, - })); + .then( + (value) => + ({ + success: true, + type, + value, + } as PromisePayload), + ) + .catch( + (error) => + ({ + success: false, + type, + error, + } as PromisePayload), + ); } }), ); @@ -151,6 +164,7 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { invalidResults.forEach((invalidResult) => { this.errorMessage = [...this.errorMessage, invalidResult.error]; }); + this.disableButtons = false; } else { if (this.languageType === 'published') { this.renderMessage(MessageType.alert, ''); @@ -158,6 +172,7 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { MessageType.success, 'Drafts created. Ready for you to publish.', ); + this.disableButtons = false; // Update languages this.resourceService .getResources('latest-drafts-translations') @@ -206,6 +221,7 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { MessageType.success, 'Drafts are successfully published.', ); + this.disableButtons = false; this.setResourceAndLoadTranslations(resource); } }) From a593420690e459811f4dcdd1a82d21f73149670f Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Tue, 9 Jan 2024 13:56:44 -0500 Subject: [PATCH 3/8] Adding get single resource --- .../service/resource/resource.service.spec.ts | 19 ++++++++++++++++++- src/app/service/resource/resource.service.ts | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/app/service/resource/resource.service.spec.ts b/src/app/service/resource/resource.service.spec.ts index f1cb5c06..753bfca7 100644 --- a/src/app/service/resource/resource.service.spec.ts +++ b/src/app/service/resource/resource.service.spec.ts @@ -4,6 +4,8 @@ import { Http, RequestOptionsArgs } from '@angular/http'; import { AuthService } from '../auth/auth.service'; import { Resource } from '../../models/resource'; import { Observable } from 'rxjs/Observable'; +import { environment } from '../../../environments/environment'; + import anything = jasmine.anything; const headers: RequestOptionsArgs = {}; @@ -16,12 +18,13 @@ class MockAuthService extends AuthService { } } -describe('ResourceService', () => { +fdescribe('ResourceService', () => { const mockHttp = new MockHttp(null, null); const mockAuthService = new MockAuthService(null, null); const service = new ResourceService(mockHttp, mockAuthService); const resource = new Resource(); + resource.id = 13; beforeEach(() => { spyOn(mockHttp, 'post').and.returnValue( @@ -38,6 +41,10 @@ describe('ResourceService', () => { spyOn(mockHttp, 'put').and.returnValue( new Observable((observer) => observer.complete()), ); + + spyOn(mockHttp, 'get').and.returnValue( + new Observable((observer) => observer.complete()), + ); }); it('creating uses authorization code', () => { @@ -51,4 +58,14 @@ describe('ResourceService', () => { expect(mockHttp.put).toHaveBeenCalledWith(anything(), anything(), headers); }); + + it('should not include "include"', () => { + service.getResource(resource.id); + expect(mockHttp.get).toHaveBeenCalledWith(`${environment.base_url}resources/${resource.id}`); + }); + + it('should include "include"', () => { + service.getResource(resource.id, 'test-data'); + expect(mockHttp.get).toHaveBeenCalledWith(`${environment.base_url}resources/${resource.id}?include=test-data`); + }); }); diff --git a/src/app/service/resource/resource.service.ts b/src/app/service/resource/resource.service.ts index 9f4ec616..34e18a71 100644 --- a/src/app/service/resource/resource.service.ts +++ b/src/app/service/resource/resource.service.ts @@ -27,6 +27,18 @@ export class ResourceService extends AbstractService { .catch(this.handleError); } + getResource(resourceId: number, include?: string): Promise { + return this.http + .get( + include ? `${this.resourcesUrl}/${resourceId}?include=${include}` : `${this.resourcesUrl}/${resourceId}`, + ) + .toPromise() + .then((response) => { + return new JsonApiDataStore().sync(response.json()); + }) + .catch(this.handleError); + } + create(resource: Resource): Promise { return this.http .post( From 825b26d40bd9a233c93650b2ac76b78ac91a96f3 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Tue, 9 Jan 2024 16:23:25 -0500 Subject: [PATCH 4/8] Adding ability to publish without Draft. Added draft/live support. Now calling resource instead of allResources when fetching publish status --- .../multiple-draft-generator.component.css | 8 ++ .../multiple-draft-generator.component.html | 61 ++++---- ...multiple-draft-generator.component.spec.ts | 8 +- .../multiple-draft-generator.component.ts | 132 +++++++++++------- .../translation/translation.component.html | 18 +-- .../translation/translation.component.ts | 89 ++++++------ src/app/models/translation.ts | 2 +- .../service/resource/resource.service.spec.ts | 10 +- src/app/service/resource/resource.service.ts | 4 +- 9 files changed, 182 insertions(+), 150 deletions(-) diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css index 9f074a8e..00a27f06 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.css @@ -7,3 +7,11 @@ .toggle-drafts label.btn { flex: 0 1 50%; } + +.translation-btn { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 350px; + margin: 0 auto 8px; +} diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html index b6edbff6..af2051fc 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.html @@ -1,57 +1,54 @@ diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts index 44a1d376..f68c58b1 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts @@ -22,7 +22,7 @@ describe('MultipleDraftGeneratorComponent', () => { const buildTranslation = ( isPublished: boolean, - generateDraft: boolean, + selectedForAction: boolean, language: string, ) => { const l = new Language(); @@ -32,7 +32,7 @@ describe('MultipleDraftGeneratorComponent', () => { t.language = l; t.is_published = isPublished; t['is-published'] = isPublished; - t.generateDraft = generateDraft; + t.selectedForAction = selectedForAction; return t; }; @@ -61,7 +61,7 @@ describe('MultipleDraftGeneratorComponent', () => { const r = new Resource(); r['latest-drafts-translations'] = translations; comp.resource = r; - comp.languageType = 'published'; + comp.actionType = 'publish'; fixture.detectChanges(); }); @@ -80,7 +80,7 @@ describe('MultipleDraftGeneratorComponent', () => { By.directive(NgbAlert), ); expect(alert.nativeElement.textContent).toContain( - `${comp.baseConfirmMessage} Chinese, French?`, + `Are you sure you want to generate a draft for these languages Chinese, French?`, ); }); }); diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts index 381a2db5..3422df54 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts @@ -23,7 +23,7 @@ interface APICall { translation: Translation; } -type LanguagesType = 'published' | 'drafts'; +type ActionType = 'publish' | 'createDrafts'; @Component({ selector: 'admin-multiple-draft-generator', @@ -33,17 +33,15 @@ type LanguagesType = 'published' | 'drafts'; export class MultipleDraftGeneratorComponent implements OnDestroy { resource: Resource; translations: Translation[]; - languageType: LanguagesType = 'published'; + actionType: ActionType = 'publish'; confirmMessage: string; errorMessage: string[]; + sucessfulMessages: string[]; alertMessage: string; sucessfulMessage: string; - checkToEnsureDraftIsPublished: number; + checkToEnsureTranslationIsPublished: number; disableButtons: boolean; - readonly baseConfirmMessage = - 'Are you sure you want to generate a draft for these languages:'; - constructor( private ngbActiveModal: NgbActiveModal, private draftService: DraftService, @@ -52,35 +50,54 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { ) {} ngOnDestroy(): void { - clearInterval(this.checkToEnsureDraftIsPublished); + clearInterval(this.checkToEnsureTranslationIsPublished); } renderMessage(type: MessageType, text: string, time?: number) { if (type === MessageType.error) { this.errorMessage = [text]; return; + } else if (type === MessageType.success) { + this.sucessfulMessages = [text]; + } else { + this[`${type}Message`] = text; + if (time) { + setTimeout(() => { + this[`${type}Message`] = ''; + }, time); + } } - this[`${type}Message`] = text; - if (time) { - setTimeout(() => { - this[`${type}Message`] = ''; - }, time); - } + } + + switchActionType(type: ActionType) { + this.actionType = type; + this.translations = []; + this.confirmMessage = ''; + this.sucessfulMessages = []; + this.alertMessage = ''; + this.disableButtons = false; + this.resource['latest-drafts-translations'].forEach((translation) => { + delete translation.selectedForAction; + }); } showConfirmAlert(): void { this.translations = this.resource['latest-drafts-translations'].filter( - (translation) => translation.generateDraft, + (translation) => translation.selectedForAction, ); if (this.translations.length === 0) { return; } - const message = this.translations + const selectedTranslations = this.translations .map((translation) => translation.language.name) .join(', '); - this.confirmMessage = `${this.baseConfirmMessage} ${message}?`; + if (this.actionType === 'publish') { + this.confirmMessage = `Are you sure you want to publish these languages: ${selectedTranslations}?`; + } else { + this.confirmMessage = `Are you sure you want to generate a draft for these languages: ${selectedTranslations}?`; + } } async publishOrCreateDrafts(): Promise { @@ -91,17 +108,14 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { // Define what promises we will call this.translations.forEach((translation) => { - if (translation['is-published'] && this.languageType === 'published') { + if (this.actionType === 'publish') { promises.push({ - type: LanguageTypeEnum.draft, + type: LanguageTypeEnum.publish, translation, }); - } else if ( - !translation['is-published'] && - this.languageType === 'drafts' - ) { + } else { promises.push({ - type: LanguageTypeEnum.publish, + type: LanguageTypeEnum.draft, translation, }); } @@ -109,10 +123,10 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { // Call promises if (promises.length) { - if (this.languageType === 'published') { - this.renderMessage(MessageType.alert, 'Creating drafts...'); + if (this.actionType === 'publish') { + this.renderMessage(MessageType.success, 'Publishing translations...'); } else { - this.renderMessage(MessageType.success, 'Publishing drafts...'); + this.renderMessage(MessageType.alert, 'Creating drafts...'); } const results: PromisePayload[] = await Promise.all( @@ -166,7 +180,19 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { }); this.disableButtons = false; } else { - if (this.languageType === 'published') { + if (this.actionType === 'publish') { + const publishingErrors = results.filter( + (result) => result.value[0]['publishing-errors'], + ); + if (publishingErrors.length) { + publishingErrors.forEach((publishingError) => { + this.errorMessage = [...this.errorMessage, publishingError.error]; + }); + } + this.checkToEnsureTranslationIsPublished = window.setInterval(() => { + this.isPublished(); + }, 5000); + } else { this.renderMessage(MessageType.alert, ''); this.renderMessage( MessageType.success, @@ -183,43 +209,46 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { setTimeout(() => { this.renderMessage(MessageType.success, ''); }, 5000); - } else { - const publishingErrors = results.filter( - (result) => result.value[0]['publishing-errors'], - ); - if (publishingErrors.length) { - publishingErrors.forEach((publishingError) => { - this.errorMessage = [...this.errorMessage, publishingError.error]; - }); - } - this.checkToEnsureDraftIsPublished = window.setInterval(() => { - this.isDraftPublished(); - }, 5000); } } } } - isDraftPublished() { + isPublished() { + this.renderMessage(MessageType.success, 'Publishing translations...'); this.resourceService - .getResources('latest-drafts-translations') - .then((resources) => { - const resource = resources.find((r) => r.id === this.resource.id); - let numberPubblished = 0; + .getResource(this.resource.id, 'latest-drafts-translations') + .then((resource) => { + let numberpublished = 0; this.translations.forEach((translation) => { const updatedTranslation = resource[ 'latest-drafts-translations' - ].find((t) => t.id === translation.id); - if (updatedTranslation) { - numberPubblished++; + ].find( + (draftTranslation) => + draftTranslation.language.id === translation.language.id, + ); + if (updatedTranslation['is-published']) { + numberpublished++; + this.sucessfulMessages = [ + ...this.sucessfulMessages, + `${translation.language.name} version ${updatedTranslation.version} has been published`, + ]; + } + if (updatedTranslation['publishing-errors']) { + clearInterval(this.checkToEnsureTranslationIsPublished); + this.errorMessage = [ + ...this.errorMessage, + updatedTranslation['publishing-errors'], + ]; + this.disableButtons = false; } }); - if (numberPubblished === this.translations.length) { - clearInterval(this.checkToEnsureDraftIsPublished); + if (numberpublished === this.translations.length) { + clearInterval(this.checkToEnsureTranslationIsPublished); this.renderMessage( MessageType.success, - 'Drafts are successfully published.', + 'All Languages are successfully published.', ); this.disableButtons = false; this.setResourceAndLoadTranslations(resource); @@ -227,6 +256,9 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { }) .catch((err) => { console.log('ERROR', err); + clearInterval(this.checkToEnsureTranslationIsPublished); + this.errorMessage = [...this.errorMessage, err]; + this.disableButtons = false; }); } diff --git a/src/app/components/translation/translation.component.html b/src/app/components/translation/translation.component.html index 3268f71f..0d00c9be 100644 --- a/src/app/components/translation/translation.component.html +++ b/src/app/components/translation/translation.component.html @@ -38,7 +38,7 @@

-
+
@@ -49,21 +49,11 @@

> Download -

+
+ -
diff --git a/src/app/components/translation/translation.component.ts b/src/app/components/translation/translation.component.ts index c6d63888..98891821 100644 --- a/src/app/components/translation/translation.component.ts +++ b/src/app/components/translation/translation.component.ts @@ -31,6 +31,7 @@ import { Observable } from 'rxjs'; import { getLatestTranslation } from './utilities'; import { environment } from '../../../environments/environment'; import { MessageType } from '../../models/message'; +import { ResourceService } from '../../service/resource/resource.service'; @Component({ selector: 'admin-translation', @@ -56,6 +57,7 @@ export class TranslationComponent implements OnInit, OnChanges, OnDestroy { private draftService: DraftService, private customManifestService: CustomManifestService, private modalService: NgbModal, + private resourceService: ResourceService, ) {} ngOnInit(): void { @@ -144,21 +146,6 @@ export class TranslationComponent implements OnInit, OnChanges, OnDestroy { return tip as Tip; } - isDraftPublished() { - try { - this.loadAllResources(); - if (this.translation.is_published) { - clearInterval(this.checkToEnsureDraftIsPublished); - this.renderMessage( - MessageType.success, - 'Draft is successfully published.', - ); - } - } catch (err) { - console.log('ERROR', err); - } - } - renderMessage(type: MessageType, text: string, time?: number) { this[`${type}Message`] = text; if (time) { @@ -168,42 +155,54 @@ export class TranslationComponent implements OnInit, OnChanges, OnDestroy { } } - async publishOrCreateDraft(): Promise { + async publish(): Promise { this.renderMessage(MessageType.error, ''); - // Create Draft - if (this.translation['is-published'] || this.translation.none) { - this.renderMessage(MessageType.alert, 'Saving...'); - this.draftService - .createDraft(this.translation) - .then(() => { - this.loadAllResources(); - this.renderMessage(MessageType.alert, ''); - this.renderMessage( - MessageType.success, - 'Draft created. Ready for you to publish.', - 5000, + this.renderMessage(MessageType.success, 'Publishing...'); + this.draftService + .publishDraft(this.resource, this.translation) + .then((data) => { + const publishingError = data[0]['publishing-errors']; + if (publishingError) { + this.renderMessage(MessageType.success, publishingError); + } + this.checkToEnsureDraftIsPublished = window.setInterval(() => { + this.isPublished(); + }, 5000); + }) + .catch(this.handleError.bind(this)); + } + + isPublished() { + try { + this.resourceService + .getResource(this.resource.id, 'latest-drafts-translations') + .then((resource) => { + const translation = resource['latest-drafts-translations'].find( + (draftTranslation) => + draftTranslation.language.id === this.translation.language.id, ); - }) - .catch(this.handleError.bind(this)); - } else { - // Publish Draft - this.renderMessage(MessageType.success, 'Publishing...'); - this.draftService - .publishDraft(this.resource, this.translation) - .then((data) => { - console.log('publishDraft.data', data); - this.renderMessage(MessageType.success, 'Draft is publishing.'); - if (data[0]['publishing-errors']) { + if (translation['publishing-errors']) { + clearInterval(this.checkToEnsureDraftIsPublished); + this.renderMessage(MessageType.success, null); + this.renderMessage( + MessageType.error, + translation['publishing-errors'], + ); + } + if (translation['is-published']) { + clearInterval(this.checkToEnsureDraftIsPublished); + this.renderMessage(MessageType.error, null); this.renderMessage( MessageType.success, - data[0]['publishing-errors'], + 'Language has been successfully published.', ); + this.loadAllResources(); } - this.checkToEnsureDraftIsPublished = window.setInterval(() => { - this.isDraftPublished(); - }, 5000); - }) - .catch(this.handleError.bind(this)); + }); + } catch (err) { + clearInterval(this.checkToEnsureDraftIsPublished); + this.renderMessage(MessageType.success, null); + this.renderMessage(MessageType.error, err.message); } } diff --git a/src/app/models/translation.ts b/src/app/models/translation.ts index 5d12769a..736ae170 100644 --- a/src/app/models/translation.ts +++ b/src/app/models/translation.ts @@ -9,7 +9,7 @@ export class Translation { resource: Resource; version: number; - generateDraft: boolean; + selectedForAction: boolean; none: boolean; static copy(translation: Translation): Translation { diff --git a/src/app/service/resource/resource.service.spec.ts b/src/app/service/resource/resource.service.spec.ts index 753bfca7..cab6493e 100644 --- a/src/app/service/resource/resource.service.spec.ts +++ b/src/app/service/resource/resource.service.spec.ts @@ -18,7 +18,7 @@ class MockAuthService extends AuthService { } } -fdescribe('ResourceService', () => { +describe('ResourceService', () => { const mockHttp = new MockHttp(null, null); const mockAuthService = new MockAuthService(null, null); const service = new ResourceService(mockHttp, mockAuthService); @@ -61,11 +61,15 @@ fdescribe('ResourceService', () => { it('should not include "include"', () => { service.getResource(resource.id); - expect(mockHttp.get).toHaveBeenCalledWith(`${environment.base_url}resources/${resource.id}`); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources/${resource.id}`, + ); }); it('should include "include"', () => { service.getResource(resource.id, 'test-data'); - expect(mockHttp.get).toHaveBeenCalledWith(`${environment.base_url}resources/${resource.id}?include=test-data`); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources/${resource.id}?include=test-data`, + ); }); }); diff --git a/src/app/service/resource/resource.service.ts b/src/app/service/resource/resource.service.ts index 34e18a71..22efc690 100644 --- a/src/app/service/resource/resource.service.ts +++ b/src/app/service/resource/resource.service.ts @@ -30,7 +30,9 @@ export class ResourceService extends AbstractService { getResource(resourceId: number, include?: string): Promise { return this.http .get( - include ? `${this.resourcesUrl}/${resourceId}?include=${include}` : `${this.resourcesUrl}/${resourceId}`, + include + ? `${this.resourcesUrl}/${resourceId}?include=${include}` + : `${this.resourcesUrl}/${resourceId}`, ) .toPromise() .then((response) => { From e2aa35fb0810ce7d4c408c750f3345fcd9e9e333 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 10 Jan 2024 09:05:51 -0500 Subject: [PATCH 5/8] Fixing tests and adding a few tests. Need to add more --- ...multiple-draft-generator.component.spec.ts | 41 +++++++-- .../translation/translation.component.spec.ts | 86 +++++++++++++++++-- 2 files changed, 113 insertions(+), 14 deletions(-) diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts index f68c58b1..3661b353 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts @@ -15,6 +15,8 @@ import { By } from '@angular/platform-browser'; import { NgbButtonLabel } from '@ng-bootstrap/ng-bootstrap'; import { Language } from '../../models/language'; import { DebugElement } from '@angular/core'; +import { TranslationVersionBadgeComponent } from '../translation/translation-version-badge/translation-version-badge.component'; +import { MessageType } from '../../models/message'; describe('MultipleDraftGeneratorComponent', () => { let comp: MultipleDraftGeneratorComponent; @@ -38,7 +40,10 @@ describe('MultipleDraftGeneratorComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [MultipleDraftGeneratorComponent], + declarations: [ + MultipleDraftGeneratorComponent, + TranslationVersionBadgeComponent, + ], imports: [NgbModule.forRoot(), FormsModule], providers: [ { provide: DraftService }, @@ -66,13 +71,13 @@ describe('MultipleDraftGeneratorComponent', () => { fixture.detectChanges(); }); - it('only shows languages without drafts', () => { + it('shows languages with and without drafts', () => { expect( fixture.debugElement.queryAll(By.directive(NgbButtonLabel)).length, - ).toBe(3); + ).toBe(4); }); - it('confirm message lists all languages', () => { + it('shows confirm message to publish selected languages', () => { comp.showConfirmAlert(); fixture.detectChanges(); @@ -80,7 +85,33 @@ describe('MultipleDraftGeneratorComponent', () => { By.directive(NgbAlert), ); expect(alert.nativeElement.textContent).toContain( - `Are you sure you want to generate a draft for these languages Chinese, French?`, + `Are you sure you want to publish these languages: Chinese, French?`, ); }); + + it('shows confirm message to create a draft for selected languages', () => { + comp.actionType = 'createDrafts'; + comp.showConfirmAlert(); + fixture.detectChanges(); + + const alert: DebugElement = fixture.debugElement.query( + By.directive(NgbAlert), + ); + expect(alert.nativeElement.textContent).toContain( + `Are you sure you want to generate a draft for these languages: Chinese, French?`, + ); + }); + + describe('Publish languages', () => { + it('shows confirm message to publish selected languages', () => { + comp.showConfirmAlert(); + fixture.detectChanges(); + spyOn(comp, 'renderMessage'); + comp.publishOrCreateDrafts(); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.success, + 'Publishing translations...', + ); + }); + }); }); diff --git a/src/app/components/translation/translation.component.spec.ts b/src/app/components/translation/translation.component.spec.ts index 24c9f8f5..2f8d6575 100644 --- a/src/app/components/translation/translation.component.spec.ts +++ b/src/app/components/translation/translation.component.spec.ts @@ -1,4 +1,11 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { + async, + ComponentFixture, + discardPeriodicTasks, + fakeAsync, + TestBed, + tick, +} from '@angular/core/testing'; import { TranslationComponent } from './translation.component'; import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { DraftService } from '../../service/draft.service'; @@ -12,10 +19,12 @@ import { Page } from '../../models/page'; import { CustomPage } from '../../models/custom-page'; import { ResourceComponent } from '../resource/resource.component'; import anything = jasmine.anything; +import { ResourceService } from '../../service/resource/resource.service'; import { CustomPageService } from '../../service/custom-page.service'; import { CustomManifestService } from '../../service/custom-manifest.service'; -import { CustomManifest } from '../../models/custom-manifest'; import { CustomTipService } from '../../service/custom-tip.service'; +import { CustomManifest } from '../../models/custom-manifest'; +import { MessageType } from '../../models/message'; import { TranslationVersionBadgeComponent } from './translation-version-badge/translation-version-badge.component'; describe('TranslationComponent', () => { @@ -26,6 +35,8 @@ describe('TranslationComponent', () => { let customTipsServiceStub; let modalServiceStub; let customManifestServiceStub; + let customDraftServiceStub; + let customResourceServiceStub; let resourceComponent: ResourceComponent; let language: Language; @@ -73,6 +84,12 @@ describe('TranslationComponent', () => { modalServiceStub = { open() {}, }; + customDraftServiceStub = { + publishDraft() {}, + }; + customResourceServiceStub = { + getResource() {}, + }; const modalRef = { componentInstance: {}, result: Promise.resolve(), @@ -87,20 +104,33 @@ describe('TranslationComponent', () => { spyOn(customManifestServiceStub, 'delete').and.returnValue( Promise.resolve(), ); + spyOn(customDraftServiceStub, 'publishDraft').and.returnValue( + Promise.resolve([ + { + 'publishing-errors': null, + }, + ]), + ); + spyOn(customResourceServiceStub, 'getResource').and.returnValue( + Promise.resolve(), + ); customPageServiceStub.delete(); modalServiceStub.open(); customManifestServiceStub.delete(); + customDraftServiceStub.publishDraft(); + customResourceServiceStub.getResource(); TestBed.configureTestingModule({ declarations: [TranslationComponent, TranslationVersionBadgeComponent], imports: [NgbModule.forRoot()], providers: [ - { provide: DraftService }, + { provide: DraftService, useValue: customDraftServiceStub }, { provide: CustomPageService, useValue: customPageServiceStub }, { provide: CustomTipService, useValue: customTipsServiceStub }, { provide: CustomManifestService, useValue: customManifestServiceStub }, { provide: NgbModal, useValue: modalServiceStub }, + { provide: ResourceService, useValue: customResourceServiceStub }, ], }).compileComponents(); })); @@ -111,6 +141,9 @@ describe('TranslationComponent', () => { resourceComponent = new ResourceComponent(null, null); comp.translationLoaded = resourceComponent.translationLoaded$; + comp.errorMessage = ''; + comp.alertMessage = ''; + comp.sucessfulMessage = ''; const pageWithCustomPage = buildPage(2); @@ -139,11 +172,11 @@ describe('TranslationComponent', () => { fixture.detectChanges(); }); - it(`should show action button with 'New Draft'`, () => { + it(`should show action button with 'Publish'`, () => { const element: DebugElement = fixture.debugElement - .queryAll(By.css('.btn.btn-secondary')) + .queryAll(By.css('.btn.btn-success')) .pop(); - expect(element.nativeElement.textContent.trim()).toBe('New Draft'); + expect(element.nativeElement.textContent.trim()).toBe('Publish'); }); it(`should show status badge with 'None'`, () => { @@ -152,6 +185,41 @@ describe('TranslationComponent', () => { ); expect(element.nativeElement.textContent).toBe('None'); }); + + describe('publish a new translation (Server creates draft)', () => { + let translation: Translation; + + beforeEach(() => { + translation = new Translation(); + translation.none = true; + translation.language = language; + translation.resource = comp.resource; + + comp.resource['latest-drafts-translations'] = [translation]; + comp.reloadTranslation(); + fixture.detectChanges(); + }); + + it('should git resource endpoint', fakeAsync(() => { + spyOn(comp, 'renderMessage'); + spyOn(comp, 'isPublished'); + comp.publish(); + + expect(comp.renderMessage).toHaveBeenCalledWith(MessageType.error, ''); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.success, + 'Publishing...', + ); + + tick(5500); + fixture.detectChanges(); + + discardPeriodicTasks(); + fixture.whenStable().then(() => { + expect(comp.isPublished).toHaveBeenCalled(); + }); + })); + }); }); describe('language has existing translation(s)', () => { @@ -286,15 +354,15 @@ describe('TranslationComponent', () => { }); describe('action button', () => { - it(`should say 'New Draft' for published translations`, () => { + it(`should say 'Publish' for published translations`, () => { translation.is_published = true; fixture.detectChanges(); const element: DebugElement = fixture.debugElement - .queryAll(By.css('.btn.btn-secondary')) + .queryAll(By.css('.btn.btn-success')) .pop(); - expect(element.nativeElement.textContent.trim()).toBe('New Draft'); + expect(element.nativeElement.textContent.trim()).toBe('Publish'); }); it(`should say 'Publish' for drafts`, () => { From 0d37d313f73d230283cee9552cab347b7ab3682c Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Tue, 23 Jan 2024 14:51:26 -0500 Subject: [PATCH 6/8] Adding more tests coverage and ensuring the code is simplified. Fixed publishing error issue on the initial Post request to publish the language. --- ...multiple-draft-generator.component.spec.ts | 98 +++++++++++++++- .../multiple-draft-generator.component.ts | 8 +- .../translation/translation.component.spec.ts | 110 +++++++++++++++++- .../translation/translation.component.ts | 4 +- .../service/resource/resource.service.spec.ts | 38 ++++-- 5 files changed, 236 insertions(+), 22 deletions(-) diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts index 3661b353..b0f3b4b5 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.spec.ts @@ -1,4 +1,10 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { + ComponentFixture, + TestBed, + discardPeriodicTasks, + fakeAsync, + tick, +} from '@angular/core/testing'; import { NgbActiveModal, NgbAlert, @@ -21,6 +27,8 @@ import { MessageType } from '../../models/message'; describe('MultipleDraftGeneratorComponent', () => { let comp: MultipleDraftGeneratorComponent; let fixture: ComponentFixture; + let customResourceServiceStub; + let customDraftServiceStub; const buildTranslation = ( isPublished: boolean, @@ -39,6 +47,39 @@ describe('MultipleDraftGeneratorComponent', () => { }; beforeEach(() => { + customResourceServiceStub = { + getResource() {}, + }; + customDraftServiceStub = { + createDraft() {}, + publishDraft() {}, + }; + + spyOn(customResourceServiceStub, 'getResource').and.returnValue( + Promise.resolve({ + 'latest-drafts-translations': [ + { + language: { id: 1 }, + 'publishing-errors': null, + 'is-published': false, + }, + ], + }), + ); + spyOn(customDraftServiceStub, 'createDraft').and.returnValue( + Promise.resolve(), + ); + spyOn(customDraftServiceStub, 'publishDraft').and.returnValue( + Promise.resolve([ + { + 'publishing-errors': null, + 'is-published': false, + }, + ]), + ); + + customResourceServiceStub.getResource(); + TestBed.configureTestingModule({ declarations: [ MultipleDraftGeneratorComponent, @@ -46,9 +87,9 @@ describe('MultipleDraftGeneratorComponent', () => { ], imports: [NgbModule.forRoot(), FormsModule], providers: [ - { provide: DraftService }, + { provide: DraftService, useValue: customDraftServiceStub }, { provide: NgbActiveModal }, - { provide: ResourceService }, + { provide: ResourceService, useValue: customResourceServiceStub }, { provide: LanguageService }, ], }).compileComponents(); @@ -102,16 +143,61 @@ describe('MultipleDraftGeneratorComponent', () => { ); }); - describe('Publish languages', () => { - it('shows confirm message to publish selected languages', () => { + describe('publishOrCreateDrafts() Publish', () => { + it('should send publish 2 languages, and call isPublished() every 5 seconds ', fakeAsync(() => { comp.showConfirmAlert(); fixture.detectChanges(); spyOn(comp, 'renderMessage'); + spyOn(comp, 'isPublished'); comp.publishOrCreateDrafts(); expect(comp.renderMessage).toHaveBeenCalledWith( MessageType.success, 'Publishing translations...', ); - }); + + tick(5500); + fixture.detectChanges(); + discardPeriodicTasks(); + + fixture.whenStable().then(() => { + expect(customDraftServiceStub.publishDraft).toHaveBeenCalledTimes(2); + expect(comp.errorMessage).toEqual([]); + expect(comp.isPublished).toHaveBeenCalledTimes(1); + + tick(5500); + fixture.detectChanges(); + discardPeriodicTasks(); + + expect(comp.isPublished).toHaveBeenCalledTimes(2); + }); + })); + + it('should return publishing errors and warn the user.', fakeAsync(() => { + customDraftServiceStub.publishDraft.and.returnValue( + Promise.resolve([ + { + 'publishing-errors': 'Error publishing...', + 'is-published': false, + }, + ]), + ); + spyOn(comp, 'renderMessage'); + spyOn(comp, 'isPublished'); + + comp.showConfirmAlert(); + fixture.detectChanges(); + comp.publishOrCreateDrafts(); + + tick(5500); + fixture.detectChanges(); + discardPeriodicTasks(); + + fixture.whenStable().then(() => { + expect(comp.errorMessage).toEqual([ + 'Error publishing...', + 'Error publishing...', + ]); + }); + })); }); }); diff --git a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts index 3422df54..aea296aa 100644 --- a/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts +++ b/src/app/components/multiple-draft-generator/multiple-draft-generator.component.ts @@ -181,12 +181,12 @@ export class MultipleDraftGeneratorComponent implements OnDestroy { this.disableButtons = false; } else { if (this.actionType === 'publish') { - const publishingErrors = results.filter( - (result) => result.value[0]['publishing-errors'], - ); + const publishingErrors = results + .filter((result) => result.value[0]['publishing-errors']) + .map((result) => result.value[0]['publishing-errors']); if (publishingErrors.length) { publishingErrors.forEach((publishingError) => { - this.errorMessage = [...this.errorMessage, publishingError.error]; + this.errorMessage = [...this.errorMessage, publishingError]; }); } this.checkToEnsureTranslationIsPublished = window.setInterval(() => { diff --git a/src/app/components/translation/translation.component.spec.ts b/src/app/components/translation/translation.component.spec.ts index 2f8d6575..c2377003 100644 --- a/src/app/components/translation/translation.component.spec.ts +++ b/src/app/components/translation/translation.component.spec.ts @@ -112,7 +112,15 @@ describe('TranslationComponent', () => { ]), ); spyOn(customResourceServiceStub, 'getResource').and.returnValue( - Promise.resolve(), + Promise.resolve({ + 'latest-drafts-translations': [ + { + language: { id: 1 }, + 'publishing-errors': null, + 'is-published': false, + }, + ], + }), ); customPageServiceStub.delete(); @@ -157,6 +165,7 @@ describe('TranslationComponent', () => { comp.language = language; const resource = new Resource(); + resource.id = 15; resource.pages = [buildPage(1), pageWithCustomPage]; resource.tips = []; resource['custom-manifests'] = [ @@ -219,6 +228,105 @@ describe('TranslationComponent', () => { expect(comp.isPublished).toHaveBeenCalled(); }); })); + + it('should clear the interval on destroy', fakeAsync(() => { + spyOn(comp, 'renderMessage'); + spyOn(comp, 'isPublished'); + spyOn(global, 'clearInterval'); + comp.publish(); + tick(5500); + fixture.detectChanges(); + discardPeriodicTasks(); + + comp.ngOnDestroy(); + fixture.whenStable().then(() => { + expect(global.clearInterval).toHaveBeenCalled(); + }); + })); + }); + }); + + describe('isPublished()', () => { + let translation: Translation; + + beforeEach(() => { + translation = new Translation(); + translation.language = language; + translation.none = true; + translation.resource = comp.resource; + comp.translation = translation; + comp.resource['latest-drafts-translations'] = [translation]; + comp.reloadTranslation(); + fixture.detectChanges(); + }); + + it('should not run clearInterval as it is not published and had no errors', () => { + spyOn(global, 'clearInterval'); + comp.isPublished(); + + expect(customResourceServiceStub.getResource).toHaveBeenCalledWith( + 15, + 'latest-drafts-translations', + ); + expect(global.clearInterval).not.toHaveBeenCalled(); + }); + + it('should run clearInterval and report pubslishing error to user', () => { + customResourceServiceStub.getResource.and.returnValue( + Promise.resolve({ + 'latest-drafts-translations': [ + { + language: { id: 1 }, + 'publishing-errors': 'Error while saving', + 'is-published': false, + }, + ], + }), + ); + spyOn(global, 'clearInterval'); + spyOn(comp, 'renderMessage'); + comp.isPublished(); + + fixture.whenStable().then(() => { + expect(global.clearInterval).toHaveBeenCalled(); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.success, + null, + ); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.error, + 'Error while saving', + ); + }); + }); + + it('should run clearInterval and report success to user', () => { + customResourceServiceStub.getResource.and.returnValue( + Promise.resolve({ + 'latest-drafts-translations': [ + { + language: { id: 1 }, + 'publishing-errors': null, + 'is-published': true, + }, + ], + }), + ); + spyOn(global, 'clearInterval'); + spyOn(comp, 'renderMessage'); + comp.isPublished(); + + fixture.whenStable().then(() => { + expect(global.clearInterval).toHaveBeenCalled(); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.error, + null, + ); + expect(comp.renderMessage).toHaveBeenCalledWith( + MessageType.success, + comp.successfullyPublishedMessage, + ); + }); }); }); diff --git a/src/app/components/translation/translation.component.ts b/src/app/components/translation/translation.component.ts index 98891821..72b3f261 100644 --- a/src/app/components/translation/translation.component.ts +++ b/src/app/components/translation/translation.component.ts @@ -50,6 +50,7 @@ export class TranslationComponent implements OnInit, OnChanges, OnDestroy { alertMessage: string; sucessfulMessage: string; checkToEnsureDraftIsPublished: number; + successfullyPublishedMessage = 'Language has been successfully published.'; constructor( private customPageService: CustomPageService, @@ -194,12 +195,13 @@ export class TranslationComponent implements OnInit, OnChanges, OnDestroy { this.renderMessage(MessageType.error, null); this.renderMessage( MessageType.success, - 'Language has been successfully published.', + this.successfullyPublishedMessage, ); this.loadAllResources(); } }); } catch (err) { + console.log('ERROR', err); clearInterval(this.checkToEnsureDraftIsPublished); this.renderMessage(MessageType.success, null); this.renderMessage(MessageType.error, err.message); diff --git a/src/app/service/resource/resource.service.spec.ts b/src/app/service/resource/resource.service.spec.ts index cab6493e..bc2d26a0 100644 --- a/src/app/service/resource/resource.service.spec.ts +++ b/src/app/service/resource/resource.service.spec.ts @@ -59,17 +59,35 @@ describe('ResourceService', () => { expect(mockHttp.put).toHaveBeenCalledWith(anything(), anything(), headers); }); - it('should not include "include"', () => { - service.getResource(resource.id); - expect(mockHttp.get).toHaveBeenCalledWith( - `${environment.base_url}resources/${resource.id}`, - ); + describe('GetResources()', () => { + it('should include "include"', () => { + service.getResources('test-data'); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources?include=test-data`, + ); + }); + + it('should not include "include"', () => { + service.getResource(resource.id); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources/${resource.id}`, + ); + }); }); - it('should include "include"', () => { - service.getResource(resource.id, 'test-data'); - expect(mockHttp.get).toHaveBeenCalledWith( - `${environment.base_url}resources/${resource.id}?include=test-data`, - ); + describe('GetResource()', () => { + it('should include "include"', () => { + service.getResource(resource.id, 'test-data'); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources/${resource.id}?include=test-data`, + ); + }); + + it('should not include "include"', () => { + service.getResource(resource.id); + expect(mockHttp.get).toHaveBeenCalledWith( + `${environment.base_url}resources/${resource.id}`, + ); + }); }); }); From 53a49701338d1feca8fbe8f571daf9dbdc7336d5 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Fri, 26 Jan 2024 14:50:36 -0500 Subject: [PATCH 7/8] Adding an error label to show if the language had a publishing error. --- .../translation-version-badge.component.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/components/translation/translation-version-badge/translation-version-badge.component.html b/src/app/components/translation/translation-version-badge/translation-version-badge.component.html index b315a268..36ecf072 100644 --- a/src/app/components/translation/translation-version-badge/translation-version-badge.component.html +++ b/src/app/components/translation/translation-version-badge/translation-version-badge.component.html @@ -1,9 +1,16 @@ {{ translation.version }} | Draft {{ translation.version }} | Live +1 | Error None From a4475ec65649abad05c711d9d3d72cf2c8d7fde7 Mon Sep 17 00:00:00 2001 From: Daniel Bisgrove Date: Wed, 27 Mar 2024 15:23:40 -0400 Subject: [PATCH 8/8] Using {{ translation.version }} instead of hardcoded number --- .../translation-version-badge.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/translation/translation-version-badge/translation-version-badge.component.html b/src/app/components/translation/translation-version-badge/translation-version-badge.component.html index 36ecf072..7cd84fa2 100644 --- a/src/app/components/translation/translation-version-badge/translation-version-badge.component.html +++ b/src/app/components/translation/translation-version-badge/translation-version-badge.component.html @@ -11,6 +11,6 @@ >{{ translation.version }} | Live 1 | Error{{ translation.version }} | Error None