diff --git a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts index bc5504bbd..6105ced38 100644 --- a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts +++ b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts @@ -13,7 +13,7 @@ import { RecentActivityListComponent } from '@osf/shared/components/recent-activ import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; import { ActivityLogsSelectors, ClearActivityLogs } from '@osf/shared/stores/activity-logs'; -import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log-with-display.mock'; +import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log.mock'; import { provideOSFCore } from '@testing/osf.testing.provider'; import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock'; import { BaseSetupOverrides, mergeSignalOverrides, provideMockStore } from '@testing/providers/store-provider.mock'; diff --git a/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts b/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts index 31e850f46..f5ee9bbad 100644 --- a/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts +++ b/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts @@ -6,10 +6,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; -import { - makeActivityLogWithDisplay, - MOCK_ACTIVITY_LOGS_WITH_DISPLAY, -} from '@testing/mocks/activity-log-with-display.mock'; +import { makeActivityLogWithDisplay, MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log.mock'; import { provideOSFCore } from '@testing/osf.testing.provider'; import { RecentActivityListComponent } from './recent-activity-list.component'; diff --git a/src/app/shared/mappers/activity-logs.mapper.ts b/src/app/shared/mappers/activity-logs.mapper.ts index ba0caf8ea..ec75f8b44 100644 --- a/src/app/shared/mappers/activity-logs.mapper.ts +++ b/src/app/shared/mappers/activity-logs.mapper.ts @@ -16,6 +16,7 @@ export class ActivityLogsMapper { type: log.type, action: log.attributes.action, date: log.attributes.date, + foreignUser: log.attributes.foreign_user, params: { contributors: contributors.length ? contributors.map((contributor) => this.fromContributorJsonApi(contributor)) diff --git a/src/app/shared/models/activity-logs/activity-logs-json-api.model.ts b/src/app/shared/models/activity-logs/activity-logs-json-api.model.ts index a63d50129..6265ba8ee 100644 --- a/src/app/shared/models/activity-logs/activity-logs-json-api.model.ts +++ b/src/app/shared/models/activity-logs/activity-logs-json-api.model.ts @@ -4,6 +4,7 @@ export interface ActivityLogJsonApi { attributes: { action: string; date: string; + foreign_user: string | null; params: { contributors?: LogContributorJsonApi[]; license?: string; diff --git a/src/app/shared/models/activity-logs/activity-logs.model.ts b/src/app/shared/models/activity-logs/activity-logs.model.ts index 1b729eaf0..1de7efa46 100644 --- a/src/app/shared/models/activity-logs/activity-logs.model.ts +++ b/src/app/shared/models/activity-logs/activity-logs.model.ts @@ -6,6 +6,7 @@ export interface ActivityLog { type: string; action: string; date: string; + foreignUser: string | null; params: { contributors: LogContributor[]; license?: string; diff --git a/src/app/shared/services/activity-logs/activity-log-formatter.service.spec.ts b/src/app/shared/services/activity-logs/activity-log-formatter.service.spec.ts new file mode 100644 index 000000000..d0f9d1204 --- /dev/null +++ b/src/app/shared/services/activity-logs/activity-log-formatter.service.spec.ts @@ -0,0 +1,310 @@ +import { TranslateService } from '@ngx-translate/core'; + +import { TestBed } from '@angular/core/testing'; + +import { makeActivityLog } from '@testing/mocks/activity-log.mock'; +import { provideOSFCore } from '@testing/osf.testing.provider'; + +import { ActivityLogFormatterService } from './activity-log-formatter.service'; +import { ActivityLogUrlBuilderService } from './activity-log-url-builder.service'; + +describe('ActivityLogFormatterService', () => { + let service: ActivityLogFormatterService; + let translateService: TranslateService; + let urlBuilder: ActivityLogUrlBuilderService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ActivityLogFormatterService, ActivityLogUrlBuilderService, provideOSFCore()], + }); + + service = TestBed.inject(ActivityLogFormatterService); + translateService = TestBed.inject(TranslateService); + urlBuilder = TestBed.inject(ActivityLogUrlBuilderService); + }); + + it('buildAnonymous returns anonymousAn when anonymous link is set', () => { + const log = makeActivityLog({ params: { anonymousLink: true } }); + + expect(service.buildAnonymous(log)).toBe('activityLog.defaults.anonymousAn'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.anonymousAn'); + }); + + it('buildAnonymous returns anonymousA when anonymous link is not set', () => { + const log = makeActivityLog(); + + expect(service.buildAnonymous(log)).toBe('activityLog.defaults.anonymousA'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.anonymousA'); + }); + + it('buildUser returns user url when embedded user is available', () => { + const log = makeActivityLog({ + embeds: { + user: { + id: 'user-1', + type: 'users', + fullName: 'Jane Doe', + givenName: 'Jane', + middleNames: '', + familyName: 'Doe', + suffix: '', + dateRegistered: '2024-01-01T00:00:00Z', + active: true, + timezone: 'UTC', + locale: 'en', + }, + }, + }); + + expect(service.buildUser(log)).toBe('Jane Doe'); + }); + + it('buildUser capitalizes foreignUser when no user url is available', () => { + const log = makeActivityLog({ foreignUser: 'githubUser' }); + + expect(service.buildUser(log)).toBe('GithubUser'); + }); + + it('buildUser returns default translation when user and foreignUser are missing', () => { + const log = makeActivityLog(); + + expect(service.buildUser(log)).toBe('activityLog.defaults.aUser'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.aUser'); + }); + + it('buildCommentLocation returns file translation when file param exists', () => { + const buildAHrefElementSpy = vi.spyOn(urlBuilder, 'buildAHrefElement'); + const log = makeActivityLog({ + params: { + file: { name: 'data.csv', url: 'files/data.csv' }, + }, + }); + + expect(service.buildCommentLocation(log)).toBe('activityLog.defaults.fileOn'); + expect(buildAHrefElementSpy).toHaveBeenCalledWith('/files/data.csv', 'data.csv'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.fileOn', { + file: 'data.csv', + }); + }); + + it('buildCommentLocation returns wiki translation when wiki param exists', () => { + const log = makeActivityLog({ + params: { + wiki: { name: 'Home', url: 'wiki/home' }, + }, + }); + + expect(service.buildCommentLocation(log)).toBe('activityLog.defaults.wikiOn'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.wikiOn', { + wiki: 'Home', + }); + }); + + it('buildCommentLocation returns empty string when file and wiki are missing', () => { + expect(service.buildCommentLocation(makeActivityLog())).toBe(''); + }); + + it('buildContributorsList returns someUsers when contributors are missing', () => { + expect(service.buildContributorsList(makeActivityLog())).toBe('activityLog.defaults.someUsers'); + }); + + it('buildContributorsList returns linked active contributors and others count', () => { + const log = makeActivityLog({ + params: { + contributors: [ + { + id: 'c-1', + fullName: 'Alice', + givenName: 'Alice', + middleNames: '', + familyName: '', + unregisteredName: null, + active: true, + }, + { + id: 'c-2', + fullName: 'Bob', + givenName: 'Bob', + middleNames: '', + familyName: '', + unregisteredName: null, + active: true, + }, + { + id: 'c-3', + fullName: 'Carol', + givenName: 'Carol', + middleNames: '', + familyName: '', + unregisteredName: null, + active: true, + }, + { + id: 'c-4', + fullName: 'Dave', + givenName: 'Dave', + middleNames: '', + familyName: '', + unregisteredName: null, + active: false, + }, + { + id: 'c-5', + fullName: 'Eve', + givenName: 'Eve', + middleNames: '', + familyName: '', + unregisteredName: null, + active: false, + }, + ], + }, + }); + + expect(service.buildContributorsList(log)).toBe( + 'Alice, Bob, CarolactivityLog.defaults.contributorsAnd 2activityLog.defaults.contributorsOthers' + ); + }); + + it('buildIdentifiers returns empty string when identifiers are missing', () => { + expect(service.buildIdentifiers(makeActivityLog())).toBe(''); + }); + + it('buildIdentifiers returns doi and ark when both exist', () => { + const log = makeActivityLog({ + params: { + identifiers: { doi: '10.1234/example', ark: 'ark:/12345' }, + }, + }); + + expect(service.buildIdentifiers(log)).toBe('doi:10.1234/example and ark:ark:/12345'); + }); + + it('buildIdentifiers returns only doi when ark is missing', () => { + const log = makeActivityLog({ + params: { + identifiers: { doi: '10.1234/example' }, + }, + }); + + expect(service.buildIdentifiers(log)).toBe('doi:10.1234/example'); + }); + + it('buildPathType returns folder for paths starting with slash', () => { + const log = makeActivityLog({ params: { path: '/folder' } }); + + expect(service.buildPathType(log)).toBe('activityLog.defaults.folder'); + }); + + it('buildPathType returns file for non-folder paths', () => { + const log = makeActivityLog({ params: { path: 'file.txt' } }); + + expect(service.buildPathType(log)).toBe('activityLog.defaults.file'); + }); + + it('buildPathType returns empty string when path is missing', () => { + expect(service.buildPathType(makeActivityLog())).toBe(''); + }); + + it('buildVersion returns version from params or hash fallback', () => { + expect(service.buildVersion(makeActivityLog({ params: { version: '3' } }))).toBe('3'); + expect(service.buildVersion(makeActivityLog())).toBe('#'); + }); + + it('buildUpdatedFields returns field translation when updated fields are missing', () => { + expect(service.buildUpdatedFields(makeActivityLog())).toBe('activityLog.defaults.field'); + }); + + it('buildUpdatedFields translates known category values', () => { + const log = makeActivityLog({ + params: { + updatedFields: { + category: { old: 'other', new: 'analysis' }, + }, + }, + }); + + expect(service.buildUpdatedFields(log)).toBe('activityLog.defaults.updatedFields'); + expect(translateService.instant).toHaveBeenCalledWith('nodeCategories.analysis'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.updatedFields', { + old: 'category', + new: 'nodeCategories.analysis', + }); + }); + + it('buildUpdatedFields returns raw new value for non-category fields', () => { + const log = makeActivityLog({ + params: { + updatedFields: { + title: { old: 'Old', new: 'New' }, + }, + }, + }); + + expect(service.buildUpdatedFields(log)).toBe('activityLog.defaults.updatedFields'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.updatedFields', { + old: 'title', + new: 'New', + }); + }); + + it('getPointerCategory returns linked node category', () => { + const log = makeActivityLog({ + embeds: { + linkedNode: { + id: 'node-1', + type: 'nodes', + title: 'Linked', + description: '', + category: 'project', + customCitation: null, + dateCreated: '2024-01-01T00:00:00Z', + dateModified: '2024-01-01T00:00:00Z', + registration: false, + preprint: false, + fork: false, + collection: false, + tags: [], + accessRequestsEnabled: false, + nodeLicense: { copyrightHolders: [], year: null }, + currentUserCanComment: false, + currentUserPermissions: [], + currentUserIsContributor: false, + currentUserIsContributorOrGroupMember: false, + wikiEnabled: false, + public: true, + subjects: [], + }, + }, + }); + + expect(service.getPointerCategory(log)).toBe('project'); + }); + + it('buildFallbackMessage uses fallbackWithNode when node url exists', () => { + const log = makeActivityLog({ + action: 'node_updated', + params: { + paramsNode: { id: 'node-1', title: 'My Project' }, + }, + }); + + expect(service.buildFallbackMessage(log)).toBe('activityLog.defaults.fallbackWithNode'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.fallbackWithNode', { + user: 'activityLog.defaults.aUser', + action: 'node updated', + node: 'My Project', + }); + }); + + it('buildFallbackMessage uses fallbackWithoutNode when node is empty', () => { + const log = makeActivityLog({ action: 'node_updated' }); + vi.spyOn(service, 'buildNode').mockReturnValue(''); + + expect(service.buildFallbackMessage(log)).toBe('activityLog.defaults.fallbackWithoutNode'); + expect(translateService.instant).toHaveBeenCalledWith('activityLog.defaults.fallbackWithoutNode', { + user: 'activityLog.defaults.aUser', + action: 'node updated', + }); + }); +}); diff --git a/src/app/shared/services/activity-logs/activity-log-formatter.service.ts b/src/app/shared/services/activity-logs/activity-log-formatter.service.ts index 7e3d026dc..f7f0d5f01 100644 --- a/src/app/shared/services/activity-logs/activity-log-formatter.service.ts +++ b/src/app/shared/services/activity-logs/activity-log-formatter.service.ts @@ -222,7 +222,16 @@ export class ActivityLogFormatterService { buildUser(log: ActivityLog): string { const userUrl = this.urlBuilder.buildUserUrl(log); - return userUrl || this.translateService.instant('activityLog.defaults.aUser'); + + if (userUrl) { + return userUrl; + } + + if (log.foreignUser) { + return this.capitalizeFirst(log.foreignUser); + } + + return this.translateService.instant('activityLog.defaults.aUser'); } buildNode(log: ActivityLog): string { @@ -278,4 +287,8 @@ export class ActivityLogFormatterService { private replaceSlash(path: string): string { return path.replace(/^\/|\/$/g, ''); } + + private capitalizeFirst(value: string): string { + return value.charAt(0).toUpperCase() + value.slice(1); + } } diff --git a/src/app/shared/services/activity-logs/activity-logs.service.spec.ts b/src/app/shared/services/activity-logs/activity-logs.service.spec.ts index ba94bb4bd..8cef5b2b6 100644 --- a/src/app/shared/services/activity-logs/activity-logs.service.spec.ts +++ b/src/app/shared/services/activity-logs/activity-logs.service.spec.ts @@ -5,11 +5,7 @@ import { inject, TestBed } from '@angular/core/testing'; import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; -import { - buildNodeLogsUrl, - buildRegistrationLogsUrl, - getActivityLogsResponse, -} from '@testing/data/activity-logs/activity-logs.data'; +import { buildNodeLogsUrl, buildRegistrationLogsUrl, getActivityLogsResponse } from '@testing/mocks/activity-log.mock'; import { provideOSFCore, provideOSFHttp } from '@testing/osf.testing.provider'; import { ActivityLogDisplayServiceMock } from '@testing/providers/activity-log-display.service.mock'; import { EnvironmentTokenMock } from '@testing/providers/environment.token.mock'; diff --git a/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts b/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts index 572ccfa9e..77c79eaca 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts @@ -6,10 +6,7 @@ import { TestBed } from '@angular/core/testing'; import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; -import { - makeActivityLogWithDisplay, - MOCK_ACTIVITY_LOGS_WITH_DISPLAY, -} from '@testing/mocks/activity-log-with-display.mock'; +import { makeActivityLogWithDisplay, MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log.mock'; import { provideOSFHttp } from '@testing/osf.testing.provider'; import { ActivityLogDisplayServiceMock } from '@testing/providers/activity-log-display.service.mock'; diff --git a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts index ac801700a..f3d110d04 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts @@ -10,7 +10,7 @@ import { CurrentResourceType } from '@shared/enums/resource-type.enum'; import { PaginatedData } from '@shared/models/paginated-data.model'; import { ActivityLogsService } from '@shared/services/activity-logs/activity-logs.service'; -import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log-with-display.mock'; +import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log.mock'; import { ClearActivityLogs, GetActivityLogs } from './activity-logs.actions'; import { ACTIVITY_LOGS_STATE_DEFAULT } from './activity-logs.model'; diff --git a/src/testing/data/activity-logs/activity-logs.data.ts b/src/testing/data/activity-logs/activity-logs.data.ts deleted file mode 100644 index 5726607eb..000000000 --- a/src/testing/data/activity-logs/activity-logs.data.ts +++ /dev/null @@ -1,51 +0,0 @@ -export const ACTIVITY_LOGS_EMBEDS_QS = - 'embed%5B%5D=original_node&embed%5B%5D=user&embed%5B%5D=linked_node&embed%5B%5D=linked_registration&embed%5B%5D=template_node'; - -export function buildRegistrationLogsUrl(registrationId: string, page: number, pageSize: number, apiBase: string) { - return `${apiBase}/v2/registrations/${registrationId}/logs/?${ACTIVITY_LOGS_EMBEDS_QS}&page=${page}&page%5Bsize%5D=${pageSize}`; -} - -export function buildNodeLogsUrl(projectId: string, page: number, pageSize: number, apiBase: string) { - return `${apiBase}/v2/nodes/${projectId}/logs/?${ACTIVITY_LOGS_EMBEDS_QS}&page=${page}&page%5Bsize%5D=${pageSize}`; -} - -type AnyObj = Record; - -export function makeActivityLog(overrides: AnyObj = {}) { - return structuredClone({ - id: 'log1', - type: 'logs', - attributes: { - action: 'update', - date: '2024-01-01T00:00:00Z', - params: {}, - }, - embeds: {}, - ...overrides, - }); -} - -export function makeActivityLogsResponse(logs: AnyObj[] = [], total?: number) { - const data = logs.length - ? logs - : [ - makeActivityLog(), - makeActivityLog({ - id: 'log2', - attributes: { action: 'create', date: '2024-01-02T00:00:00Z', params: {} }, - }), - ]; - - return structuredClone({ - data, - meta: { - total: total ?? data.length, - anonymous: false, - }, - included: null, - }); -} - -export function getActivityLogsResponse() { - return makeActivityLogsResponse(); -} diff --git a/src/testing/mocks/activity-log-with-display.mock.ts b/src/testing/mocks/activity-log-with-display.mock.ts deleted file mode 100644 index a64c8b367..000000000 --- a/src/testing/mocks/activity-log-with-display.mock.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; - -export function makeActivityLogWithDisplay(overrides: Partial = {}): ActivityLogWithDisplay { - return structuredClone({ - id: 'log1', - type: 'logs', - action: 'update', - date: '2024-01-01T00:00:00Z', - params: { - contributors: [], - paramsNode: { id: 'node1', title: 'Test Node' }, - paramsProject: null, - pointer: null, - }, - formattedActivity: 'Test activity', - ...overrides, - }); -} - -export const MOCK_ACTIVITY_LOGS_WITH_DISPLAY: ActivityLogWithDisplay[] = [ - makeActivityLogWithDisplay({ - id: 'log1', - action: 'update', - date: '2024-01-01T00:00:00Z', - formattedActivity: 'Test activity 1', - }), - makeActivityLogWithDisplay({ - id: 'log2', - action: 'create', - date: '2024-01-02T00:00:00Z', - params: { - contributors: [], - paramsNode: { id: 'node2', title: 'Test Node 2' }, - paramsProject: null, - pointer: null, - }, - formattedActivity: 'Test activity 2', - }), -]; diff --git a/src/testing/mocks/activity-log.mock.ts b/src/testing/mocks/activity-log.mock.ts new file mode 100644 index 000000000..8388183c3 --- /dev/null +++ b/src/testing/mocks/activity-log.mock.ts @@ -0,0 +1,118 @@ +import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; +import { ActivityLog } from '@osf/shared/models/activity-logs/activity-logs.model'; + +export const ACTIVITY_LOGS_EMBEDS_QS = + 'embed%5B%5D=original_node&embed%5B%5D=user&embed%5B%5D=linked_node&embed%5B%5D=linked_registration&embed%5B%5D=template_node'; + +export function buildRegistrationLogsUrl(registrationId: string, page: number, pageSize: number, apiBase: string) { + return `${apiBase}/v2/registrations/${registrationId}/logs/?${ACTIVITY_LOGS_EMBEDS_QS}&page=${page}&page%5Bsize%5D=${pageSize}`; +} + +export function buildNodeLogsUrl(projectId: string, page: number, pageSize: number, apiBase: string) { + return `${apiBase}/v2/nodes/${projectId}/logs/?${ACTIVITY_LOGS_EMBEDS_QS}&page=${page}&page%5Bsize%5D=${pageSize}`; +} + +type ActivityLogJsonApiOverrides = Record; + +export function makeActivityLogJsonApi(overrides: ActivityLogJsonApiOverrides = {}) { + return structuredClone({ + id: 'log1', + type: 'logs', + attributes: { + action: 'update', + date: '2024-01-01T00:00:00Z', + params: {}, + }, + embeds: {}, + ...overrides, + }); +} + +export function makeActivityLogsResponse(logs: ActivityLogJsonApiOverrides[] = [], total?: number) { + const data = logs.length + ? logs + : [ + makeActivityLogJsonApi(), + makeActivityLogJsonApi({ + id: 'log2', + attributes: { action: 'create', date: '2024-01-02T00:00:00Z', params: {} }, + }), + ]; + + return structuredClone({ + data, + meta: { + total: total ?? data.length, + anonymous: false, + }, + included: null, + }); +} + +export function getActivityLogsResponse() { + return makeActivityLogsResponse(); +} + +export type ActivityLogOverrides = Partial> & { + params?: Partial; +}; + +export function makeActivityLog(overrides: ActivityLogOverrides = {}): ActivityLog { + const { params: paramsOverrides, ...restOverrides } = overrides; + + return structuredClone({ + id: 'log-1', + type: 'logs', + action: 'node_updated', + date: '2024-01-01T00:00:00Z', + foreignUser: null, + params: { + contributors: [], + paramsNode: { id: '', title: '' }, + paramsProject: null, + pointer: null, + ...paramsOverrides, + }, + ...restOverrides, + }); +} + +export type ActivityLogWithDisplayOverrides = ActivityLogOverrides & { + formattedActivity?: ActivityLogWithDisplay['formattedActivity']; +}; + +export function makeActivityLogWithDisplay(overrides: ActivityLogWithDisplayOverrides = {}): ActivityLogWithDisplay { + const { params: paramsOverrides, ...restOverrides } = overrides; + const formattedActivity = 'formattedActivity' in overrides ? overrides.formattedActivity : 'Test activity'; + + return { + ...makeActivityLog({ + id: 'log1', + action: 'update', + params: { + paramsNode: { id: 'node1', title: 'Test Node' }, + ...paramsOverrides, + }, + ...restOverrides, + }), + formattedActivity, + }; +} + +export const MOCK_ACTIVITY_LOGS_WITH_DISPLAY: ActivityLogWithDisplay[] = [ + makeActivityLogWithDisplay({ + id: 'log1', + action: 'update', + date: '2024-01-01T00:00:00Z', + formattedActivity: 'Test activity 1', + }), + makeActivityLogWithDisplay({ + id: 'log2', + action: 'create', + date: '2024-01-02T00:00:00Z', + params: { + paramsNode: { id: 'node2', title: 'Test Node 2' }, + }, + formattedActivity: 'Test activity 2', + }), +];