From 1a2acdb9c5f7b21f10637b78953982e30137ed5a Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Thu, 18 Jun 2026 04:34:17 +0000 Subject: [PATCH 01/12] add configurable-actions in publisheddata --- CI/e2e/frontend.config.e2e.json | 64 ++++++++ .../e2e/published-data/published-data.cy.js | 13 +- .../schema/frontend.config.jsonforms.json | 147 ++++++++++++++++++ src/app/app-config.service.spec.ts | 3 +- src/app/app-config.service.ts | 2 + .../publisheddata-details.component.html | 34 +--- .../publisheddata-details.component.scss | 6 + .../publisheddata-details.component.spec.ts | 43 ++--- .../publisheddata-details.component.ts | 30 ++-- .../configurable-action.component.ts | 13 ++ .../configurable-action.interfaces.ts | 8 +- 11 files changed, 289 insertions(+), 74 deletions(-) diff --git a/CI/e2e/frontend.config.e2e.json b/CI/e2e/frontend.config.e2e.json index 1c2d3c223a..b64db52c06 100644 --- a/CI/e2e/frontend.config.e2e.json +++ b/CI/e2e/frontend.config.e2e.json @@ -295,6 +295,70 @@ "target": "_blank" } ], + "publishedDataActionsEnabled": true, + "publishedDataActions": [ + { + "id": "publish-published-data", + "description": "Publish published data", + "order": 1, + "label": "Publish", + "type": "xhr", + "mat_icon": "publish", + "method": "PATCH", + "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData0Status" + }, + "enabled": "@status === 'private'", + "payload": "{\"isPublished\":\"true\"}", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + }, + "authorization": ["true"] + }, + { + "id": "register-published-data", + "description": "Register published data", + "order": 2, + "label": "Register", + "type": "xhr", + "mat_icon": "registration", + "method": "PATCH", + "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData0Status" + }, + "enabled": "@status === 'public'", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + }, + "authorization": ["true"] + }, + { + "id": "amend-published-data", + "description": "Amend published data", + "order": 3, + "label": "Amend", + "type": "xhr", + "mat_icon": "edit", + "method": "PATCH", + "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData0Status" + }, + "enabled": "@status === 'registered'", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + }, + "authorization": ["true"] + } + ], "datasetDetailsActionsEnabled": false, "datasetDetailsActions": [], "selectionActionsEnabled": true, diff --git a/cypress/e2e/published-data/published-data.cy.js b/cypress/e2e/published-data/published-data.cy.js index 6c3be903c7..aad68978b2 100644 --- a/cypress/e2e/published-data/published-data.cy.js +++ b/cypress/e2e/published-data/published-data.cy.js @@ -205,9 +205,12 @@ describe("Datasets general", () => { cy.get('[data-cy="status"]').contains("private"); - cy.get('[data-cy="publishButton"]').click(); + cy.get("configurable-action button").contains("Publish").click(); - cy.get("simple-snack-bar").should("contain", "Publishing Failed."); + cy.get("simple-snack-bar").should( + "contain", + "error performing the action", + ); }); it("admins should be able to edit their private published data", () => { @@ -365,7 +368,7 @@ describe("Datasets general", () => { cy.get('[data-cy="status"]').contains("private"); - cy.get('[data-cy="publishButton"]').click(); + cy.get("configurable-action button").contains("Publish").click(); cy.get('[data-cy="status"]').contains("public"); }); @@ -436,7 +439,7 @@ describe("Datasets general", () => { cy.get('[data-cy="status"]').contains("public"); - cy.get('[data-cy="registerButton"]').click(); + cy.get("configurable-action button").contains("Register").click(); cy.get('[data-cy="status"]').contains("registered"); }); @@ -525,7 +528,7 @@ describe("Datasets general", () => { cy.get('[data-cy="status"]').contains("private"); - cy.get('[data-cy="publishButton"]').click(); + cy.get("configurable-action button").contains("Publish").click(); cy.get('[data-cy="status"]').contains("public"); diff --git a/src/app/admin/schema/frontend.config.jsonforms.json b/src/app/admin/schema/frontend.config.jsonforms.json index 715ec1d693..4331f4c4a2 100644 --- a/src/app/admin/schema/frontend.config.jsonforms.json +++ b/src/app/admin/schema/frontend.config.jsonforms.json @@ -182,6 +182,62 @@ } } }, + "publishedDataActionsEnabled": { "type": "boolean" }, + "publishedDataActions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { "type": "string" }, + "id": { "type": "string" }, + "description": { "type": "string" }, + "order": { "type": "number" }, + "mat_icon": { "type": "string" }, + "url": { "type": "string" }, + "target": { "type": "string" }, + "enabled": { "type": "string" }, + "authorization": { "type": "array", "items": { "type": "string" } }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { "type": "string" }, + "value": { "type": "string" } + } + } + }, + "variables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { "type": "string" }, + "value": { "type": "string" } + } + } + }, + "headers": { + "type": "object", + "properties": { + "content-type": { "type": "string" }, + "Authorization": { "type": "string" } + } + }, + "method": { + "type": "string", + "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] + }, + "type": { + "type": "string", + "enum": ["form", "xhr", "link", "json-download"] + }, + "icon": { "type": "string" }, + "payload": { "type": "string" }, + "filename": { "type": "string" } + } + } + }, "defaultDatasetsListSettings": { "type": "object", @@ -712,6 +768,97 @@ } ] }, + { + "type": "Group", + "label": "Published Data Actions", + "options": { + "expandable": true + }, + "elements": [ + { + "type": "Control", + "scope": "#/properties/publishedDataActionsEnabled" + }, + { + "type": "ListWithDetail", + "scope": "#/properties/publishedDataActions", + "options": { + "detail": { + "type": "VerticalLayout", + "elements": [ + { "type": "Control", "scope": "#/properties/label" }, + { "type": "Control", "scope": "#/properties/id" }, + { + "type": "Control", + "scope": "#/properties/description" + }, + { "type": "Control", "scope": "#/properties/order" }, + { "type": "Control", "scope": "#/properties/mat_icon" }, + { "type": "Control", "scope": "#/properties/url" }, + { "type": "Control", "scope": "#/properties/target" }, + { "type": "Control", "scope": "#/properties/enabled" }, + { + "type": "Control", + "scope": "#/properties/method" + }, + { "type": "Control", "scope": "#/properties/type" }, + { "type": "Control", "scope": "#/properties/icon" }, + { + "type": "Control", + "scope": "#/properties/payload", + "options": { "multi": true } + }, + { "type": "Control", "scope": "#/properties/filename" }, + { + "type": "Control", + "scope": "#/properties/authorization" + }, + { + "type": "Control", + "scope": "#/properties/variables", + "options": { + "detail": { + "type": "HorizontalLayout", + "elements": [ + { "type": "Control", "scope": "#/properties/key" }, + { "type": "Control", "scope": "#/properties/value" } + ] + } + } + }, + { + "type": "Control", + "scope": "#/properties/inputs", + "options": { + "detail": { + "type": "HorizontalLayout", + "elements": [ + { "type": "Control", "scope": "#/properties/key" }, + { "type": "Control", "scope": "#/properties/value" } + ] + } + } + }, + { + "type": "Group", + "label": "Headers", + "elements": [ + { + "type": "Control", + "scope": "#/properties/headers/properties/content-type" + }, + { + "type": "Control", + "scope": "#/properties/headers/properties/Authorization" + } + ] + } + ] + } + } + } + ] + }, { "type": "Group", "label": "Localization", diff --git a/src/app/app-config.service.spec.ts b/src/app/app-config.service.spec.ts index 0457973d88..42e2e85fd8 100644 --- a/src/app/app-config.service.spec.ts +++ b/src/app/app-config.service.spec.ts @@ -5,7 +5,6 @@ import { AppConfigService, HelpMessages, } from "app-config.service"; -import { time } from "node:console"; import { Observable, of } from "rxjs"; import { MockHttp } from "shared/MockStubs"; @@ -95,6 +94,8 @@ const appConfig: AppConfigInterface = { datafilesActions: [], datasetSelectionActionsEnabled: false, datasetSelectionActions: [], + publishedDataActionsEnabled: false, + publishedDataActions: [], defaultDatasetsListSettings: { columns: [ { diff --git a/src/app/app-config.service.ts b/src/app/app-config.service.ts index 5f74d73224..e6c04990f8 100644 --- a/src/app/app-config.service.ts +++ b/src/app/app-config.service.ts @@ -108,6 +108,8 @@ export interface AppConfigInterface { datasetDetailsActions: ActionConfig[]; datasetSelectionActionsEnabled: boolean; datasetSelectionActions: ActionConfig[]; + publishedDataActions: ActionConfig[]; + publishedDataActionsEnabled: boolean; editDatasetEnabled: boolean; editDatasetSampleEnabled: boolean; editMetadataEnabled: boolean; diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html index 0100f54db5..7449f43f64 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html @@ -26,33 +26,13 @@ - - - - + + + diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.scss b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.scss index 6167f01611..e2af89a3ea 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.scss +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.scss @@ -30,3 +30,9 @@ mat-card { margin: 0 1em; } } + +configurable-actions { + display: flex; + flex-direction: row-reverse; + max-width: 80%; +} diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.spec.ts b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.spec.ts index e14a0557e0..6c322094f1 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.spec.ts +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.spec.ts @@ -2,8 +2,10 @@ import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { PublisheddataDetailsComponent } from "./publisheddata-details.component"; import { MockActivatedRoute, + MockAuthService, MockPublishedDataApi, MockRouter, + MockUserApi, } from "shared/MockStubs"; import { provideMockStore, MockStore } from "@ngrx/store/testing"; import { NgxJsonViewerModule } from "ngx-json-viewer"; @@ -17,17 +19,29 @@ import { AppConfigService } from "app-config.service"; import { PublishedData, PublishedDataService, + UsersService, } from "@scicatproject/scicat-sdk-ts-angular"; -import { selectCurrentPublishedData } from "state-management/selectors/published-data.selectors"; +import { AuthService } from "shared/services/auth/auth.service"; import { - selectIsAdmin, - selectIsLoggedIn, -} from "state-management/selectors/user.selectors"; + provideHttpClient, + withInterceptorsFromDi, +} from "@angular/common/http"; +import { selectCurrentPublishedData } from "state-management/selectors/published-data.selectors"; +import { selectIsAdmin } from "state-management/selectors/user.selectors"; const getConfig = () => ({ editMetadataEnabled: true, editPublishedData: true, jsonMetadataEnabled: true, + publishedDataActionsEnabled: true, + publishedDataActions: [ + { + id: "test-action", + label: "Test Action", + type: "link", + url: "https://example.com", + }, + ], }); const createPublishedData = (status: string) => @@ -55,10 +69,13 @@ describe("PublisheddataDetailsComponent", () => { SharedScicatFrontendModule, ], providers: [ + provideHttpClient(withInterceptorsFromDi()), provideMockStore(), { provide: Router, useClass: MockRouter }, { provide: ActivatedRoute, useClass: MockActivatedRoute }, { provide: PublishedDataService, useClass: MockPublishedDataApi }, + { provide: UsersService, useClass: MockUserApi }, + { provide: AuthService, useClass: MockAuthService }, { provide: AppConfigService, useValue: { getConfig } }, ], }).compileComponents(); @@ -69,7 +86,6 @@ describe("PublisheddataDetailsComponent", () => { createPublishedData("public"), ); store.overrideSelector(selectIsAdmin, false); - store.overrideSelector(selectIsLoggedIn, false); })); beforeEach(() => { @@ -82,21 +98,10 @@ describe("PublisheddataDetailsComponent", () => { expect(component).toBeTruthy(); }); - describe("login state", () => { - it("should hide status actions when the user is not logged in", () => { - const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('[data-cy="registerButton"]')).toBeNull(); - }); - - it("should show status actions when the user is logged in", () => { - store.overrideSelector(selectIsLoggedIn, true); - store.refreshState(); - fixture.detectChanges(); - + describe("configurable actions", () => { + it("should render configurable-actions element when publishedDataActions are configured", () => { const compiled = fixture.debugElement.nativeElement; - expect( - compiled.querySelector('[data-cy="registerButton"]'), - ).not.toBeNull(); + expect(compiled.querySelector("configurable-actions")).not.toBeNull(); }); }); diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts index b33db46c6a..61ee4cbeae 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts @@ -3,12 +3,9 @@ import { PublishedData } from "@scicatproject/scicat-sdk-ts-angular"; import { Store } from "@ngrx/store"; import { ActivatedRoute, Router } from "@angular/router"; import { - amendPublishedDataAction, deletePublishedDataAction, fetchPublishedDataAction, fetchRelatedDatasetsAndAddToBatchAction, - publishPublishedDataAction, - registerPublishedDataAction, } from "state-management/actions/published-data.actions"; import { Subscription } from "rxjs"; import { pluck } from "rxjs/operators"; @@ -16,8 +13,8 @@ import { selectCurrentPublishedData } from "state-management/selectors/published import { AppConfigService } from "app-config.service"; import { selectIsAdmin, - selectIsLoggedIn, } from "state-management/selectors/user.selectors"; +import { ActionItems } from "shared/modules/configurable-actions/configurable-action.interfaces"; @Component({ selector: "publisheddata-details", @@ -30,12 +27,16 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { isAdmin$ = this.store.select(selectIsAdmin); publishedData: PublishedData & { metadata?: any }; subscriptions: Subscription[] = []; - isLoggedIn$ = this.store.select(selectIsLoggedIn); appConfig = this.appConfigService.getConfig(); show = false; landingPageUrl = ""; doi = ""; + actionItems: ActionItems = { + datasets: [], + publisheddata: [], + }; + constructor( private appConfigService: AppConfigService, private route: ActivatedRoute, @@ -55,6 +56,7 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { this.currentData$.subscribe((data) => { if (data) { this.publishedData = data; + this.actionItems.publisheddata[0] = data; if (this.appConfig.landingPage) { this.landingPageUrl = @@ -69,30 +71,18 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } - onRegisterClick(doi: string) { - if ( - confirm( - "Are you sure you want to register this published data? Keep in mind that no further changes can be made after this action.", - ) - ) { - this.store.dispatch(registerPublishedDataAction({ doi })); + onActionFinished(event: { success: boolean }) { + if (event.success) { + this.store.dispatch(fetchPublishedDataAction({ id: this.doi })); } } - onAmendClick(doi: string) { - this.store.dispatch(amendPublishedDataAction({ doi })); - } - onDeleteClick(doi: string) { if (confirm("Are you sure you want to delete this published data?")) { this.store.dispatch(deletePublishedDataAction({ doi })); } } - onPublishClick(doi: string) { - this.store.dispatch(publishPublishedDataAction({ doi })); - } - onEditClick() { const id = encodeURIComponent(this.doi); this.router.navigateByUrl("/publishedDatasets/" + id + "/edit"); diff --git a/src/app/shared/modules/configurable-actions/configurable-action.component.ts b/src/app/shared/modules/configurable-actions/configurable-action.component.ts index 0dfb36efae..3c2c71cec5 100644 --- a/src/app/shared/modules/configurable-actions/configurable-action.component.ts +++ b/src/app/shared/modules/configurable-actions/configurable-action.component.ts @@ -170,6 +170,15 @@ export class ConfigurableActionComponent return _.get(this.actionItems, `instruments[${index}].${field}`); } + const publishedDataFieldMatch = selector.match( + /^#PublishedData\[(\d+)\]Field\[(\w+)\]$/, + ); + if (publishedDataFieldMatch) { + const index = Number(publishedDataFieldMatch[1]); + const field = publishedDataFieldMatch[2]; + return _.get(this.actionItems, `publisheddata[${index}].${field}`); + } + return undefined; } @@ -212,6 +221,10 @@ export class ConfigurableActionComponent .flatMap("files") .filter("selected") .sumBy((f) => Number(f.size || 0)), + "#PublishedData0Doi": () => + _.get(this.actionItems, "publisheddata[0].doi"), + "#PublishedData0Status": () => + _.get(this.actionItems, "publisheddata[0].status"), }; return staticMap; } diff --git a/src/app/shared/modules/configurable-actions/configurable-action.interfaces.ts b/src/app/shared/modules/configurable-actions/configurable-action.interfaces.ts index 167486474a..d15a9bd51d 100644 --- a/src/app/shared/modules/configurable-actions/configurable-action.interfaces.ts +++ b/src/app/shared/modules/configurable-actions/configurable-action.interfaces.ts @@ -1,7 +1,10 @@ import { DataFiles_File } from "datasets/datafiles/datafiles.interfaces"; -import { Instrument } from "@scicatproject/scicat-sdk-ts-angular"; +import { + DatasetClass, + Instrument, + PublishedData, +} from "@scicatproject/scicat-sdk-ts-angular"; import { DynamicField } from "../dialog/dialog.component"; -import { DatasetClass } from "@scicatproject/scicat-sdk-ts-angular"; export type DialogField = { key: string } & DynamicField; @@ -45,6 +48,7 @@ export type ActionItemDataset = DatasetClass & { export interface ActionItems { datasets: ActionItemDataset[]; + publisheddata?: PublishedData[]; instruments?: Instrument[]; [key: string]: unknown; } From 1a6ea865c6641e05e36f66e7aed60a83dc262420 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Thu, 18 Jun 2026 04:47:37 +0000 Subject: [PATCH 02/12] eslint error fix --- .../publisheddata-details/publisheddata-details.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts index 61ee4cbeae..311a96069f 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts @@ -11,9 +11,7 @@ import { Subscription } from "rxjs"; import { pluck } from "rxjs/operators"; import { selectCurrentPublishedData } from "state-management/selectors/published-data.selectors"; import { AppConfigService } from "app-config.service"; -import { - selectIsAdmin, -} from "state-management/selectors/user.selectors"; +import { selectIsAdmin } from "state-management/selectors/user.selectors"; import { ActionItems } from "shared/modules/configurable-actions/configurable-action.interfaces"; @Component({ From 928f0727428c10c306d61b8b04dc9e7eff93f2a3 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Fri, 19 Jun 2026 12:45:21 +0000 Subject: [PATCH 03/12] fix e2e config json and published-data cyress test --- CI/e2e/frontend.config.e2e.json | 21 ++++++++----------- .../e2e/published-data/published-data.cy.js | 5 +---- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/CI/e2e/frontend.config.e2e.json b/CI/e2e/frontend.config.e2e.json index b64db52c06..4272da2bf7 100644 --- a/CI/e2e/frontend.config.e2e.json +++ b/CI/e2e/frontend.config.e2e.json @@ -304,8 +304,8 @@ "label": "Publish", "type": "xhr", "mat_icon": "publish", - "method": "PATCH", - "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/publish", "variables": { "doi": "#PublishedData0Doi", "status": "#PublishedData0Status" @@ -315,8 +315,7 @@ "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" - }, - "authorization": ["true"] + } }, { "id": "register-published-data", @@ -325,8 +324,8 @@ "label": "Register", "type": "xhr", "mat_icon": "registration", - "method": "PATCH", - "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/register", "variables": { "doi": "#PublishedData0Doi", "status": "#PublishedData0Status" @@ -335,8 +334,7 @@ "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" - }, - "authorization": ["true"] + } }, { "id": "amend-published-data", @@ -345,8 +343,8 @@ "label": "Amend", "type": "xhr", "mat_icon": "edit", - "method": "PATCH", - "url": "http://localhost:3000/api/v3/publisheddata/{{ @doi }}/", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/amend", "variables": { "doi": "#PublishedData0Doi", "status": "#PublishedData0Status" @@ -355,8 +353,7 @@ "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" - }, - "authorization": ["true"] + } } ], "datasetDetailsActionsEnabled": false, diff --git a/cypress/e2e/published-data/published-data.cy.js b/cypress/e2e/published-data/published-data.cy.js index aad68978b2..ac9ae497bc 100644 --- a/cypress/e2e/published-data/published-data.cy.js +++ b/cypress/e2e/published-data/published-data.cy.js @@ -207,10 +207,7 @@ describe("Datasets general", () => { cy.get("configurable-action button").contains("Publish").click(); - cy.get("simple-snack-bar").should( - "contain", - "error performing the action", - ); + cy.get("simple-snack-bar").should("contain", "Action failed"); }); it("admins should be able to edit their private published data", () => { From 9e98fdd466a85ed22266945ae88fea7bc920a08c Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Fri, 19 Jun 2026 13:24:38 +0000 Subject: [PATCH 04/12] add publishedDataActions in config.json, too. --- src/assets/config.json | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/assets/config.json b/src/assets/config.json index 9fc3ee8a68..ad48c3faee 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -301,6 +301,87 @@ "datasetDetailsActions": [], "selectionActionsEnabled": true, "selectionActions": [], + "publishedDataActionsEnabled": true, + "publishedDataActions": [ + { + "id": "3a954df5-6a4b-4159-ad95-4d9717891932", + "description": "Make a published data record private", + "order": 1, + "label": "Private", + "type": "xhr", + "mat_icon": "public_off", + "method": "PATCH", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}", + "payload": "{\"status\": \"private\"}", + "enabled": "@status == 'public'", + "authorization": "#datasetOwner || #userIsAdmin", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData[0]Field[status]" + }, + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + } + }, + { + "id": "9a67e562-2f08-4637-a8e4-a36cd6cfe6a1", + "description": "Publish published data", + "order": 2, + "label": "Publish", + "type": "xhr", + "mat_icon": "publish", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/publish", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData[0]Field[status]" + }, + "enabled": "@status === 'private'", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + } + }, + { + "id": "5d6f59b4-11c4-4a52-b21c-92fc5834c2ed", + "description": "Register published data", + "order": 3, + "label": "Register", + "type": "xhr", + "mat_icon": "registration", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/register", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData[0]Field[status]" + }, + "enabled": "@status === 'public'", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + } + }, + { + "id": "b3a11f37-aed5-466a-87b4-4e66dd488621", + "description": "Amend published data", + "order": 4, + "label": "Amend", + "type": "xhr", + "mat_icon": "edit", + "method": "POST", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/amend", + "variables": { + "doi": "#PublishedData0Doi", + "status": "#PublishedData[0]Field[status]" + }, + "enabled": "@status === 'registered'", + "headers": { + "Content-Type": "application/json", + "Authorization": "#tokenBearer" + } + } + ], "labelMaps": { "filters": { "LocationFilter": "Location", From 31d82928c132d75d1556a41d5dd7669ed028d2da Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Fri, 10 Jul 2026 14:14:07 +0200 Subject: [PATCH 05/12] Fixed for style, add user and encodedDoi as variable --- .../publisheddata-details.component.html | 1 + .../publisheddata-details.component.ts | 32 +++++++++++++++---- .../configurable-actions.component.ts | 5 ++- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html index 7449f43f64..c26a4136d6 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.html @@ -31,6 +31,7 @@ *ngIf="appConfig.publishedDataActionsEnabled" [actionsConfig]="appConfig.publishedDataActions" [actionItems]="actionItems" + [buttonsStyle]="actionButtonsStyle" (actionFinished)="onActionFinished($event)"> diff --git a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts index 311a96069f..9f7212c415 100644 --- a/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts +++ b/src/app/publisheddata/publisheddata-details/publisheddata-details.component.ts @@ -1,5 +1,8 @@ import { Component, OnInit, OnDestroy } from "@angular/core"; -import { PublishedData } from "@scicatproject/scicat-sdk-ts-angular"; +import { + PublishedData, + ReturnedUserDto, +} from "@scicatproject/scicat-sdk-ts-angular"; import { Store } from "@ngrx/store"; import { ActivatedRoute, Router } from "@angular/router"; import { @@ -7,12 +10,18 @@ import { fetchPublishedDataAction, fetchRelatedDatasetsAndAddToBatchAction, } from "state-management/actions/published-data.actions"; -import { Subscription } from "rxjs"; +import { Subscription, combineLatest } from "rxjs"; import { pluck } from "rxjs/operators"; import { selectCurrentPublishedData } from "state-management/selectors/published-data.selectors"; import { AppConfigService } from "app-config.service"; -import { selectIsAdmin } from "state-management/selectors/user.selectors"; -import { ActionItems } from "shared/modules/configurable-actions/configurable-action.interfaces"; +import { + selectCurrentUser, + selectIsAdmin, +} from "state-management/selectors/user.selectors"; +import { + ActionItems, + ActionButtonStyle, +} from "shared/modules/configurable-actions/configurable-action.interfaces"; @Component({ selector: "publisheddata-details", @@ -27,6 +36,7 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { subscriptions: Subscription[] = []; appConfig = this.appConfigService.getConfig(); show = false; + user: ReturnedUserDto | undefined; landingPageUrl = ""; doi = ""; @@ -34,6 +44,7 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { datasets: [], publisheddata: [], }; + actionButtonsStyle: ActionButtonStyle = { raised: true, color: "accent" }; constructor( private appConfigService: AppConfigService, @@ -51,10 +62,19 @@ export class PublisheddataDetailsComponent implements OnInit, OnDestroy { ); this.subscriptions.push( - this.currentData$.subscribe((data) => { + combineLatest([ + this.store.select(selectCurrentUser), + this.currentData$, + ]).subscribe(([user, data]) => { + this.user = user; if (data) { this.publishedData = data; - this.actionItems.publisheddata[0] = data; + this.actionItems = { + datasets: [], + publisheddata: [data], + encodedDoi: encodeURIComponent(data.doi), + user, + }; if (this.appConfig.landingPage) { this.landingPageUrl = diff --git a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts index e1010209bf..f4db36a06c 100644 --- a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts +++ b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts @@ -29,7 +29,10 @@ export class ConfigurableActionsComponent { constructor(public appConfigService: AppConfigService) {} get visible(): boolean { - return this.appConfigService.getConfig().datafilesActionsEnabled; + return ( + this.appConfigService.getConfig().datafilesActionsEnabled || + this.actionsConfig.length > 0 + ); } get maxFileSize(): number { From 013c523f1549489cfc27caae5c3694a10acc2fe7 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Mon, 13 Jul 2026 04:23:18 +0000 Subject: [PATCH 06/12] fix configurable-actions visible condition and e2e tests config. --- CI/e2e/frontend.config.e2e.json | 20 +++++++++---------- .../configurable-actions.component.ts | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CI/e2e/frontend.config.e2e.json b/CI/e2e/frontend.config.e2e.json index 4272da2bf7..f87c4e7423 100644 --- a/CI/e2e/frontend.config.e2e.json +++ b/CI/e2e/frontend.config.e2e.json @@ -305,10 +305,10 @@ "type": "xhr", "mat_icon": "publish", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/publish", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/publish", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData0Status" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'private'", "payload": "{\"isPublished\":\"true\"}", @@ -325,10 +325,10 @@ "type": "xhr", "mat_icon": "registration", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/register", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/register", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData0Status" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'public'", "headers": { @@ -344,10 +344,10 @@ "type": "xhr", "mat_icon": "edit", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/amend", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/amend", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData0Status" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'registered'", "headers": { @@ -1009,4 +1009,4 @@ "enabled": false, "htmlContent": "Here you can place your custom info/about content for CI testing" } -} +} \ No newline at end of file diff --git a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts index f4db36a06c..bd47824496 100644 --- a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts +++ b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts @@ -30,8 +30,8 @@ export class ConfigurableActionsComponent { get visible(): boolean { return ( - this.appConfigService.getConfig().datafilesActionsEnabled || - this.actionsConfig.length > 0 + this.appConfigService.getConfig().datafilesActionsEnabled && + (this.actionsConfig?.length ?? 0) > 0 ); } From 3f69ac757822ba70d4f90ce60797be79a20d300d Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Mon, 13 Jul 2026 04:43:53 +0000 Subject: [PATCH 07/12] default frontend.config.json consistency. --- src/assets/config.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/assets/config.json b/src/assets/config.json index ad48c3faee..380c1e86a2 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -332,10 +332,10 @@ "type": "xhr", "mat_icon": "publish", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/publish", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/publish", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData[0]Field[status]" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'private'", "headers": { @@ -351,10 +351,10 @@ "type": "xhr", "mat_icon": "registration", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/register", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/register", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData[0]Field[status]" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'public'", "headers": { @@ -370,10 +370,10 @@ "type": "xhr", "mat_icon": "edit", "method": "POST", - "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}/amend", + "url": "http://localhost:3000/api/v4/publisheddata/{{ @encodedDoi }}/amend", "variables": { - "doi": "#PublishedData0Doi", - "status": "#PublishedData[0]Field[status]" + "encodedDoi": "#encodedDoi", + "status": "#publisheddata[0].status" }, "enabled": "@status === 'registered'", "headers": { @@ -1030,4 +1030,4 @@ "enabled": false, "htmlContent": "Here you can place your custom info/about content" } -} +} \ No newline at end of file From c4b1bfe999e26f16ee60f75c6969b9e61c3fef95 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Tue, 21 Jul 2026 17:24:26 +0000 Subject: [PATCH 08/12] add new variable #publishedDataOwner to viewHandlers --- .../configurable-action.component.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/app/shared/modules/configurable-actions/configurable-action.component.ts b/src/app/shared/modules/configurable-actions/configurable-action.component.ts index 3c2c71cec5..7d064c7c20 100644 --- a/src/app/shared/modules/configurable-actions/configurable-action.component.ts +++ b/src/app/shared/modules/configurable-actions/configurable-action.component.ts @@ -11,6 +11,7 @@ import { import { DatePipe } from "@angular/common"; import { DatasetClass, + PublishedData, UsersService, } from "@scicatproject/scicat-sdk-ts-angular"; import { @@ -233,6 +234,7 @@ export class ConfigurableActionComponent let expr = condition; const symbols: Record = { "#datasetOwner": "context.isOwner", + "#publishedDataOwner": "context.isPublishedDataOwner", "#userIsAdmin": "context.isAdmin", "#isPublished": String( this.actionItems.datasets?.[0]?.isPublished === true, @@ -288,6 +290,7 @@ export class ConfigurableActionComponent context: { isAdmin: this.isAdmin, isOwner: this.isDatasetOwner, + isPublishedDataOwner: this.isPublishedDataOwner, maxSize: this.configService.getConfig().maxDirectDownloadSize, }, }; @@ -305,6 +308,16 @@ export class ConfigurableActionComponent return _.some(datasets, (d) => userGroups.includes(d.ownerGroup)); } + private get isPublishedDataOwner(): boolean { + const publishedData = _.get( + this.actionItems, + "publisheddata", + [], + ) as PublishedData[]; + const username = _.get(this.actionItems, "user.username") as string; + return _.some(publishedData, (pd) => pd.createdBy === username); + } + private buildDependenciesGraph( variables: Record, ): Record> { From e5d5e5abad2976b61b4eb0c3261ba7318d4bb405 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Tue, 21 Jul 2026 17:36:41 +0000 Subject: [PATCH 09/12] add tests for publisheddata configurable action --- .../configurable-action.component.spec.ts | 75 +++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/src/app/shared/modules/configurable-actions/configurable-action.component.spec.ts b/src/app/shared/modules/configurable-actions/configurable-action.component.spec.ts index 816837a713..83d895dfbe 100644 --- a/src/app/shared/modules/configurable-actions/configurable-action.component.spec.ts +++ b/src/app/shared/modules/configurable-actions/configurable-action.component.spec.ts @@ -44,6 +44,7 @@ import { selectProfile, } from "state-management/selectors/user.selectors"; import { ActionConfig, ActionItems } from "./configurable-action.interfaces"; +import { PublishedData } from "@scicatproject/scicat-sdk-ts-angular"; describe("1000: ConfigurableActionComponent", () => { let component: ConfigurableActionComponent; @@ -1189,7 +1190,69 @@ describe("1000: ConfigurableActionComponent", () => { expect(component.disabled).toBeFalse(); }); - it("1240: #isPublished token should follow dataset publish status", () => { + it("1240: #publishedDataOwner token should enable action for creator", () => { + store.overrideSelector(selectProfile, mockUserProfiles[0]); + store.overrideSelector(selectIsAdmin, false); + store.refreshState(); + + const publishedDataConfig: ActionConfig = { + ...mockActionsConfig[0], + id: "published-data-owner-enabled-test", + enabled: "#publishedDataOwner", + variables: {}, + }; + + const publishedDataItems: ActionItems = { + datasets: [], + publisheddata: [ + { + createdBy: "testuser", + doi: "10.1234/test", + title: "Test", + abstract: "Abstract", + datasetPids: [], + status: "public", + } as PublishedData, + ], + user: { username: "testuser" }, + }; + + createComponent(publishedDataConfig, publishedDataItems); + expect(component.disabled).toBeFalse(); + }); + + it("1250: #publishedDataOwner token should NOT enable action for non-creator", () => { + store.overrideSelector(selectProfile, mockUserProfiles[0]); + store.overrideSelector(selectIsAdmin, false); + store.refreshState(); + + const publishedDataConfig: ActionConfig = { + ...mockActionsConfig[0], + id: "published-data-owner-disabled-test", + enabled: "#publishedDataOwner", + variables: {}, + }; + + const publishedDataItems: ActionItems = { + datasets: [], + publisheddata: [ + { + createdBy: "otheruser", + doi: "10.1234/test", + title: "Test", + abstract: "Abstract", + datasetPids: [], + status: "public", + } as PublishedData, + ], + user: { username: "testuser" }, + }; + + createComponent(publishedDataConfig, publishedDataItems); + expect(component.disabled).toBeTrue(); + }); + + it("1260: #isPublished token should follow dataset publish status", () => { const publishedItems: ActionItems = { datasets: structuredClone(mockActionItemsDatafilesNofiles.datasets), }; @@ -1206,7 +1269,7 @@ describe("1000: ConfigurableActionComponent", () => { expect(component.disabled).toBeFalse(); }); - it("1250: #!isPublished token should follow dataset publish status", () => { + it("1270: #!isPublished token should follow dataset publish status", () => { const unpublishedItems: ActionItems = { datasets: structuredClone(mockActionItemsDatafilesNofiles.datasets), }; @@ -1223,7 +1286,7 @@ describe("1000: ConfigurableActionComponent", () => { expect(component.disabled).toBeFalse(); }); - it("1260: #Length should evaluate selected file list length", () => { + it("1280: #Length should evaluate selected file list length", () => { const lengthConfig: ActionConfig = { ...mockActionsConfig[0], id: "length-enabled-test", @@ -1240,7 +1303,7 @@ describe("1000: ConfigurableActionComponent", () => { expect(component.disabled).toBeFalse(); }); - it("1270: #MaxDownloadableSize should compare against configured max size", () => { + it("1290: #MaxDownloadableSize should compare against configured max size", () => { mockAppConfigService.appConfig.maxDirectDownloadSize = lowerMaxFileSizeLimit; @@ -1262,7 +1325,7 @@ describe("1000: ConfigurableActionComponent", () => { expect(component.disabled).toBeFalse(); }); - it("1280: hidden expression should hide action when condition is true", () => { + it("1300: hidden expression should hide action when condition is true", () => { const hiddenConfig: ActionConfig = { ...mockActionsConfig[0], id: "hidden-test", @@ -1405,7 +1468,7 @@ describe("1000: ConfigurableActionComponent", () => { ]; allKeywordMapSelectors.forEach((testCase) => { - it(`1290: ${testCase.name} selector should resolve`, () => { + it(`1310: ${testCase.name} selector should resolve`, () => { const selectorConfig: ActionConfig = { ...mockActionsConfig[0], id: `selector-${testCase.name}`, From b3fce193cc6ad1b4379b94166112eadb0609e2d3 Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Tue, 21 Jul 2026 17:46:03 +0000 Subject: [PATCH 10/12] fix enabled logic for publishedDataActions components. --- src/assets/config.json | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/assets/config.json b/src/assets/config.json index 380c1e86a2..3ef3ff5734 100644 --- a/src/assets/config.json +++ b/src/assets/config.json @@ -313,8 +313,7 @@ "method": "PATCH", "url": "http://localhost:3000/api/v4/publisheddata/{{ @doi }}", "payload": "{\"status\": \"private\"}", - "enabled": "@status == 'public'", - "authorization": "#datasetOwner || #userIsAdmin", + "enabled": "(#publishedDataOwner || #userIsAdmin) && @status === 'public'", "variables": { "doi": "#PublishedData0Doi", "status": "#PublishedData[0]Field[status]" @@ -337,7 +336,7 @@ "encodedDoi": "#encodedDoi", "status": "#publisheddata[0].status" }, - "enabled": "@status === 'private'", + "enabled": "(#publishedDataOwner || #userIsAdmin) && @status === 'private'", "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" @@ -356,7 +355,7 @@ "encodedDoi": "#encodedDoi", "status": "#publisheddata[0].status" }, - "enabled": "@status === 'public'", + "enabled": "(#publishedDataOwner || #userIsAdmin) && @status === 'public'", "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" @@ -375,7 +374,7 @@ "encodedDoi": "#encodedDoi", "status": "#publisheddata[0].status" }, - "enabled": "@status === 'registered'", + "enabled": "(#publishedDataOwner || #userIsAdmin) && @status === 'registered'", "headers": { "Content-Type": "application/json", "Authorization": "#tokenBearer" From d13e44c21a35c56e4323744726fc5e9e1adc8d8a Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Wed, 22 Jul 2026 12:59:44 +0000 Subject: [PATCH 11/12] fix visibility of configurable-actions components --- src/app/datasets/datafiles/datafiles.component.html | 1 + .../dataset-detail-dynamic.component.html | 1 + .../configurable-actions/configurable-actions.component.ts | 5 +---- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/datasets/datafiles/datafiles.component.html b/src/app/datasets/datafiles/datafiles.component.html index 50ec74edd9..4eff294a43 100644 --- a/src/app/datasets/datafiles/datafiles.component.html +++ b/src/app/datasets/datafiles/datafiles.component.html @@ -33,6 +33,7 @@

No files associated to this dataset

diff --git a/src/app/datasets/dataset-detail/dataset-detail-dynamic/dataset-detail-dynamic.component.html b/src/app/datasets/dataset-detail/dataset-detail-dynamic/dataset-detail-dynamic.component.html index 6988b26179..c370085249 100644 --- a/src/app/datasets/dataset-detail/dataset-detail-dynamic/dataset-detail-dynamic.component.html +++ b/src/app/datasets/dataset-detail/dataset-detail-dynamic/dataset-detail-dynamic.component.html @@ -14,6 +14,7 @@ diff --git a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts index bd47824496..d7c532dd0e 100644 --- a/src/app/shared/modules/configurable-actions/configurable-actions.component.ts +++ b/src/app/shared/modules/configurable-actions/configurable-actions.component.ts @@ -29,10 +29,7 @@ export class ConfigurableActionsComponent { constructor(public appConfigService: AppConfigService) {} get visible(): boolean { - return ( - this.appConfigService.getConfig().datafilesActionsEnabled && - (this.actionsConfig?.length ?? 0) > 0 - ); + return (this.actionsConfig?.length ?? 0) > 0; } get maxFileSize(): number { From 1b13a1d671570fe2734f1e8e86b42759db0ac9bc Mon Sep 17 00:00:00 2001 From: Johannes Reppin Date: Wed, 22 Jul 2026 13:25:15 +0000 Subject: [PATCH 12/12] fix tests on configurable actions to reflect new visibility logic --- .../configurable-actions.component.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/modules/configurable-actions/configurable-actions.component.spec.ts b/src/app/shared/modules/configurable-actions/configurable-actions.component.spec.ts index 8b8222cb5b..6bd38eaa88 100644 --- a/src/app/shared/modules/configurable-actions/configurable-actions.component.spec.ts +++ b/src/app/shared/modules/configurable-actions/configurable-actions.component.spec.ts @@ -80,13 +80,13 @@ describe("1010: ConfigurableActionsComponent", () => { } }); - it("0020: actions should be not visible when disabled in configuration", () => { - mockAppConfigService.appConfig.datafilesActionsEnabled = false; + it("0020: actions should be not visible when config array is empty", () => { + component.actionsConfig = []; expect(component.visible).toEqual(false); }); - it("0030: actions should be visible when enabled in configuration", () => { - mockAppConfigService.appConfig.datafilesActionsEnabled = true; + it("0030: actions should be visible when config array is non-empty", () => { + component.actionsConfig = mockActionsConfig; expect(component.visible).toEqual(true); });