diff --git a/apps/gauzy/jest.config.ts b/apps/gauzy/jest.config.ts index 7f6d198d7f..fda6364795 100644 --- a/apps/gauzy/jest.config.ts +++ b/apps/gauzy/jest.config.ts @@ -4,7 +4,8 @@ export default { preset: '../../jest.preset.js', // Moved here from project.json's deprecated `setupFile` executor option. The target itself pointed // at a `jest.config.js` that does not exist, so none of this app's specs had ever run. - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../jest.angular-defaults.ts'], coverageDirectory: '../../coverage/apps/gauzy', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/apps/gauzy/src/app/app.component.spec.ts b/apps/gauzy/src/app/app.component.spec.ts index acfaaeb0c5..e7651bda72 100644 --- a/apps/gauzy/src/app/app.component.spec.ts +++ b/apps/gauzy/src/app/app.component.spec.ts @@ -1,13 +1,20 @@ import { TestBed } from '@angular/core/testing'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { RouterModule } from '@angular/router'; +import { CoreModule, LanguagesService } from '@gauzy/ui-core/core'; +import { ComponentsModule } from '@gauzy/ui-core/shared'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ + // AppModule is the root module (BrowserModule), so it cannot be imported here. This is what + // it gives the root component: ui-core's CoreModule.forRoot() (analytics, SEO, Jitsu, auth), + // the router outlet, the loading skeleton, and LanguagesService (ThemeModule's provider). declarations: [AppComponent], teardown: { destroyAfterEach: false }, - imports: [], - providers: [provideHttpClient(withInterceptorsFromDi())] + imports: [CoreModule.forRoot(), RouterModule, ComponentsModule], + providers: [provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), LanguagesService] }).compileComponents(); }); it('should create the app', () => { diff --git a/apps/gauzy/src/app/pages/employees/activity/app-url-activity/app-url-activity.component.spec.ts b/apps/gauzy/src/app/pages/employees/activity/app-url-activity/app-url-activity.component.spec.ts index 6d1ee59dc2..c560b020d8 100644 --- a/apps/gauzy/src/app/pages/employees/activity/app-url-activity/app-url-activity.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/activity/app-url-activity/app-url-activity.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppUrlActivityComponent } from './app-url-activity.component'; +import { ActivityModule } from '../activity.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('AppUrlActivityComponent', () => { let component: AppUrlActivityComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [AppUrlActivityComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [ActivityModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(AppUrlActivityComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/employees/activity/screenshot/screenshot/screenshot.component.spec.ts b/apps/gauzy/src/app/pages/employees/activity/screenshot/screenshot/screenshot.component.spec.ts index f49aed1fee..588734742a 100644 --- a/apps/gauzy/src/app/pages/employees/activity/screenshot/screenshot/screenshot.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/activity/screenshot/screenshot/screenshot.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ScreenshotComponent } from './screenshot.component'; +import { ScreenshotModule } from '../screenshot.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('ScreenshotComponent', () => { let component: ScreenshotComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ScreenshotComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [ScreenshotModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(ScreenshotComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/employees/activity/time-activities/time-activities/time-activities.component.spec.ts b/apps/gauzy/src/app/pages/employees/activity/time-activities/time-activities/time-activities.component.spec.ts index 7e011c784d..2cd9ca22d5 100644 --- a/apps/gauzy/src/app/pages/employees/activity/time-activities/time-activities/time-activities.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/activity/time-activities/time-activities/time-activities.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimeActivitiesComponent } from './time-activities.component'; +import { TimeAndActivitiesModule } from '../time-activities.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('TimeActivitiesComponent', () => { let component: TimeActivitiesComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TimeActivitiesComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [TimeAndActivitiesModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(TimeActivitiesComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/employees/timesheet/calendar/calendar/calendar.component.spec.ts b/apps/gauzy/src/app/pages/employees/timesheet/calendar/calendar/calendar.component.spec.ts index 90db8500ab..18fd410207 100644 --- a/apps/gauzy/src/app/pages/employees/timesheet/calendar/calendar/calendar.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/timesheet/calendar/calendar/calendar.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CalendarComponent } from './calendar.component'; +import { CalendarModule } from '../calendar.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('CalendarComponent', () => { let component: CalendarComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [CalendarComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [CalendarModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(CalendarComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/employees/timesheet/daily/daily/daily.component.spec.ts b/apps/gauzy/src/app/pages/employees/timesheet/daily/daily/daily.component.spec.ts index e3f52a9f26..e6bb924e73 100644 --- a/apps/gauzy/src/app/pages/employees/timesheet/daily/daily/daily.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/timesheet/daily/daily/daily.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DailyComponent } from './daily.component'; +import { DailyModule } from '../daily.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('DailyComponent', () => { let component: DailyComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [DailyComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [DailyModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(DailyComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/employees/timesheet/layout/layout.component.spec.ts b/apps/gauzy/src/app/pages/employees/timesheet/layout/layout.component.spec.ts index d1b534d10b..508fb494f4 100644 --- a/apps/gauzy/src/app/pages/employees/timesheet/layout/layout.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/timesheet/layout/layout.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimesheetLayoutComponent } from './layout.component'; +import { TimesheetModule } from '../timesheet.module'; +import { ActivatedRoute } from '@angular/router'; describe('TimesheetLayoutComponent', () => { let component: TimesheetLayoutComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TimesheetLayoutComponent], + // Not standalone: import its NgModule. The route (timesheet.routes.ts) gives it `data.tabsetId`; without one the tab registry rejects the tabs. + imports: [TimesheetModule], + providers: [{ provide: ActivatedRoute, useValue: { snapshot: { data: { tabsetId: 'timesheet-page' } } } }], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/apps/gauzy/src/app/pages/employees/timesheet/view/view/view.component.spec.ts b/apps/gauzy/src/app/pages/employees/timesheet/view/view/view.component.spec.ts index b8ca7cbeba..f03c39a956 100644 --- a/apps/gauzy/src/app/pages/employees/timesheet/view/view/view.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/timesheet/view/view/view.component.spec.ts @@ -1,16 +1,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ViewComponent } from './view.component'; -describe('ViewComponent', () => { - let component: ViewComponent; - let fixture: ComponentFixture; +import { TimesheetViewComponent } from './view.component'; +import { ViewModule } from '../view.module'; +describe('TimesheetViewComponent', () => { + let component: TimesheetViewComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ViewComponent], + // Not standalone: import its NgModule. (This stub imported `ViewComponent`; the file exports `TimesheetViewComponent`, so it declared `undefined`.) + imports: [ViewModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(ViewComponent); + fixture = TestBed.createComponent(TimesheetViewComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/apps/gauzy/src/app/pages/employees/timesheet/weekly/weekly/weekly.component.spec.ts b/apps/gauzy/src/app/pages/employees/timesheet/weekly/weekly/weekly.component.spec.ts index 62b6a361ff..999d35b0a6 100644 --- a/apps/gauzy/src/app/pages/employees/timesheet/weekly/weekly/weekly.component.spec.ts +++ b/apps/gauzy/src/app/pages/employees/timesheet/weekly/weekly/weekly.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { WeeklyComponent } from './weekly.component'; +import { WeeklyModule } from '../weekly.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('WeeklyComponent', () => { let component: WeeklyComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [WeeklyComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [WeeklyModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(WeeklyComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/goals/goals.component.spec.ts b/apps/gauzy/src/app/pages/goals/goals.component.spec.ts index ef8e880c8c..8bedc3f731 100644 --- a/apps/gauzy/src/app/pages/goals/goals.component.spec.ts +++ b/apps/gauzy/src/app/pages/goals/goals.component.spec.ts @@ -5,7 +5,8 @@ describe('GoalsComponent', () => { let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [GoalsComponent], + // Standalone: it is imported, not declared. + imports: [GoalsComponent], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/apps/gauzy/src/app/pages/reports/amounts-owed-report/amounts-owed-report/amounts-owed-report.component.spec.ts b/apps/gauzy/src/app/pages/reports/amounts-owed-report/amounts-owed-report/amounts-owed-report.component.spec.ts index 334768bcc0..a312e6e50e 100644 --- a/apps/gauzy/src/app/pages/reports/amounts-owed-report/amounts-owed-report/amounts-owed-report.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/amounts-owed-report/amounts-owed-report/amounts-owed-report.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AmountsOwedReportComponent } from './amounts-owed-report.component'; +import { AmountsOwedReportModule } from '../amounts-owed-report.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('AmountsOwedReportComponent', () => { let component: AmountsOwedReportComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [AmountsOwedReportComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [AmountsOwedReportModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(AmountsOwedReportComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/apps-urls-report/apps-urls-report/apps-urls-report.component.spec.ts b/apps/gauzy/src/app/pages/reports/apps-urls-report/apps-urls-report/apps-urls-report.component.spec.ts index fa761ebb36..e2100931e2 100644 --- a/apps/gauzy/src/app/pages/reports/apps-urls-report/apps-urls-report/apps-urls-report.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/apps-urls-report/apps-urls-report/apps-urls-report.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AppsUrlsReportComponent } from './apps-urls-report.component'; +import { AppsUrlsReportModule } from '../apps-urls-report.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('AppsUrlsReportComponent', () => { let component: AppsUrlsReportComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [AppsUrlsReportComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [AppsUrlsReportModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(AppsUrlsReportComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.spec.ts b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.spec.ts index 06e6a97ef0..ec02cc2dea 100644 --- a/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/expenses-report/expenses-report/expenses-report.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ExpensesReportComponent } from './expenses-report.component'; +import { ExpensesReportModule } from '../expenses-report.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('ExpensesReportComponent', () => { let component: ExpensesReportComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ExpensesReportComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [ExpensesReportModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(ExpensesReportComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.spec.ts b/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.spec.ts index 69f664e0c9..987ff38eba 100644 --- a/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/manual-time/manual-time/manual-time.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ManualTimeComponent } from './manual-time.component'; +import { ManualTimeModule } from '../manual-time.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('ManualTimeComponent', () => { let component: ManualTimeComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ManualTimeComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [ManualTimeModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(ManualTimeComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/payment-report/payment-report/payment-report.component.spec.ts b/apps/gauzy/src/app/pages/reports/payment-report/payment-report/payment-report.component.spec.ts index 5e603b6dab..f44eafdd0b 100644 --- a/apps/gauzy/src/app/pages/reports/payment-report/payment-report/payment-report.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/payment-report/payment-report/payment-report.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PaymentReportComponent } from './payment-report.component'; +import { PaymentReportModule } from '../payment-report.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('PaymentReportComponent', () => { let component: PaymentReportComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [PaymentReportComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [PaymentReportModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(PaymentReportComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/time-limit-report/time-limit-report/time-limit-report.component.spec.ts b/apps/gauzy/src/app/pages/reports/time-limit-report/time-limit-report/time-limit-report.component.spec.ts index b6e87b4838..a632048047 100644 --- a/apps/gauzy/src/app/pages/reports/time-limit-report/time-limit-report/time-limit-report.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/time-limit-report/time-limit-report/time-limit-report.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimeLimitReportComponent } from './time-limit-report.component'; +import { TimeLimitReportModule } from '../time-limit-report.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('TimeLimitReportComponent', () => { let component: TimeLimitReportComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TimeLimitReportComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [TimeLimitReportModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(TimeLimitReportComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/reports/time-reports/time-reports/time-reports.component.spec.ts b/apps/gauzy/src/app/pages/reports/time-reports/time-reports/time-reports.component.spec.ts index 9bde1d8288..15846cd05a 100644 --- a/apps/gauzy/src/app/pages/reports/time-reports/time-reports/time-reports.component.spec.ts +++ b/apps/gauzy/src/app/pages/reports/time-reports/time-reports/time-reports.component.spec.ts @@ -1,15 +1,21 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimeReportsComponent } from './time-reports.component'; +import { TimeReportsModule } from '../time-reports.module'; +import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG } from '@gauzy/ui-core/core'; describe('TimeReportsComponent', () => { let component: TimeReportsComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TimeReportsComponent], + // Not standalone: import the NgModule that declares it, for the template's real scope. + imports: [TimeReportsModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { + // In the app the page's route data sets this before the page renders; the template reads + // `(datePickerConfig$ | async).isSaveDatePicker`, which is null until something does. + TestBed.inject(DateRangePickerBuilderService).setDatePickerConfig(DEFAULT_DATE_PICKER_CONFIG); fixture = TestBed.createComponent(TimeReportsComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.spec.ts b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.spec.ts index 9e6520fb85..0586e23d76 100644 --- a/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.spec.ts +++ b/apps/gauzy/src/app/pages/tasks/components/my-task-dialog/my-task-dialog.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MyTaskDialogComponent } from './my-task-dialog.component'; +import { TasksModule } from '../../tasks.module'; +import { EmployeesService } from '@gauzy/ui-core/core'; describe('MyTaskDialogComponent', () => { let component: MyTaskDialogComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [MyTaskDialogComponent], + // Not standalone: import its NgModule. EmployeesService is provided by the host feature modules in the app. + imports: [TasksModule], + providers: [EmployeesService], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/jest.angular-defaults.ts b/jest.angular-defaults.ts new file mode 100644 index 0000000000..8f661187c5 --- /dev/null +++ b/jest.angular-defaults.ts @@ -0,0 +1,98 @@ +/** + * Default TestBed environment for the Angular Jest projects — a `setupFilesAfterEnv` entry listed + * AFTER each project's own `src/test-setup.ts` (which starts the zone test environment). + * + * Why: most component specs in the Angular projects are the `should create` stubs the Angular CLI + * generated, and none of them had ever run (the Unit Tests workflow is new, and until the harness + * fixes that come with this file every Angular suite failed to load). Written against an empty + * testing module, they fail on the first injection the component makes — `TranslateService`, + * `NbToastrService`, `NbDialogRef`, `ActivatedRoute`, the `translate` pipe — which is app-wide + * infrastructure, not what those specs are about. This file provides that infrastructure the way + * the app's root module does, once, instead of pasting the same provider list into ~100 specs. + * + * How: a top-level `beforeEach` adds these imports/providers to the testing module before any spec's + * own `beforeEach`. `TestBed.configureTestingModule` MERGES successive calls, and a later provider + * wins, so a spec that provides its own `TranslateService`, `NbDialogRef` or route stub still gets + * its own. Nothing is compiled until a spec asks TestBed for something, so specs that never touch + * TestBed pay only the import cost. + * + * Deliberately NOT here: feature services and stores (a spec that needs one should say so), and + * `NO_ERRORS_SCHEMA` (the zone test env errors on unknown elements, and that stays on). Keep the + * imports to third-party modules and leaf workspace modules: this file loads BEFORE each spec, so + * importing e.g. `@gauzy/ui-core/core` here would cache the real modules that a spec later replaces + * with `jest.mock(...)` (chat-sidebar.service.spec does), and the mock would silently not apply. + */ +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; +import { provideRouter } from '@angular/router'; +import { + NbDatepickerModule, + NbDialogModule, + NbDialogRef, + NbMenuModule, + NbSidebarModule, + NbThemeModule, + NbToastrModule, + NbWindowModule +} from '@nebular/theme'; +import { TranslateModule } from '@ngx-translate/core'; +import { NgxPermissionsModule } from 'ngx-permissions'; +import { environment, GAUZY_ENV } from '@gauzy/ui-config'; +import { TablerIconsModule } from '@gauzy/ui-core/icons'; + +// jsdom implements no `matchMedia`; Nebular's layout/breakpoint services and several components call +// it. Browsers always have it. An inert stand-in that matches nothing (a spec that cares can replace it). +if (typeof window !== 'undefined' && typeof window.matchMedia !== 'function') { + Object.defineProperty(window, 'matchMedia', { + writable: true, + configurable: true, + value: (query: string): MediaQueryList => + ({ + matches: false, + media: query, + onchange: null, + addListener: () => undefined, + removeListener: () => undefined, + addEventListener: () => undefined, + removeEventListener: () => undefined, + dispatchEvent: () => false + }) as MediaQueryList + }); +} + +/** What a component opened by `NbDialogService` receives. Specs that assert on `close` spy on it. */ +const dialogRefStub: Partial> = { + close: () => undefined +}; + +beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + // No loader: `translate` returns the key, which is all a unit test should depend on. + TranslateModule.forRoot(), + NbThemeModule.forRoot({ name: 'default' }), + NbDialogModule.forRoot(), + NbToastrModule.forRoot(), + NbWindowModule.forRoot(), + NbSidebarModule.forRoot(), + NbMenuModule.forRoot(), + NbDatepickerModule.forRoot(), + // The app root module's forRoot; feature modules only call forChild. + NgxPermissionsModule.forRoot(), + // Registers the default icon pack ('eva', mapped to Tabler); without it every throws + // "Default pack is not registered". + TablerIconsModule + ], + providers: [ + provideHttpClient(), + // Requests are recorded, never sent; nothing is flushed unless a spec does it. + provideHttpClientTesting(), + provideRouter([]), + provideNoopAnimations(), + { provide: NbDialogRef, useValue: dialogRefStub }, + { provide: GAUZY_ENV, useValue: environment } + ] + }); +}); diff --git a/jest.preset.js b/jest.preset.js index 1322f3ffd2..970f4f01fa 100644 --- a/jest.preset.js +++ b/jest.preset.js @@ -48,6 +48,16 @@ module.exports = { transformIgnorePatterns, // Nx's resolver plus one rule: a Jest project sees ONE copy of `@angular/*`. See the file. resolver: require.resolve('./jest.resolver.js'), + // ngx-daterangepicker-material (ui-core's date-range selector) imports `dayjs/esm` and its plugins + // from an `.mjs` bundle. Transforming that ESM tree is not enough: an `.mjs` importer gets Node's + // default-import semantics (the whole module object), so `dayjs.extend` came out undefined and + // every suite reaching the selector failed to load. The package's CommonJS build is the same code + // with `module.exports = dayjs`, which is what those semantics expect. (Jest merges a preset's + // `moduleNameMapper` with a project's own.) + moduleNameMapper: { + '^dayjs/esm$': 'dayjs', + '^dayjs/esm/(.*)$': 'dayjs/$1' + }, // `moment` loads with both `import moment from` and `import * as moment from`, as the bundlers // allow. Jest CONCATENATES a preset's `setupFiles` with a project's own, so no project loses it. setupFiles: [require.resolve('./jest.interop.js')] diff --git a/nx.json b/nx.json index 350250cc5d..5da65317a5 100644 --- a/nx.json +++ b/nx.json @@ -181,7 +181,8 @@ "^production", "{workspaceRoot}/jest.preset.js", "{workspaceRoot}/jest.resolver.js", - "{workspaceRoot}/jest.interop.js" + "{workspaceRoot}/jest.interop.js", + "{workspaceRoot}/jest.angular-defaults.ts" ], "options": { "passWithNoTests": true diff --git a/packages/desktop-ui-lib/jest.config.ts b/packages/desktop-ui-lib/jest.config.ts index 3b49456c3a..75630e10a3 100644 --- a/packages/desktop-ui-lib/jest.config.ts +++ b/packages/desktop-ui-lib/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'desktop-ui-lib', preset: '../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../jest.angular-defaults.ts'], coverageDirectory: '../../coverage/packages/desktop-ui-lib', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/desktop-ui-lib/src/lib/dialogs/alert/alert.component.spec.ts b/packages/desktop-ui-lib/src/lib/dialogs/alert/alert.component.spec.ts index 305c10776b..f2905ea524 100644 --- a/packages/desktop-ui-lib/src/lib/dialogs/alert/alert.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/dialogs/alert/alert.component.spec.ts @@ -11,6 +11,8 @@ describe('AlertComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(AlertComponent); component = fixture.componentInstance; + // Opened by NbDialogService with a `data` context; the template reads it unguarded. + component.data = { title: 'Title', message: 'Message', status: 'info' }; fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/screen-capture/screen-capture.component.spec.ts b/packages/desktop-ui-lib/src/lib/screen-capture/screen-capture.component.spec.ts index 18bbf87af3..5c10a00134 100644 --- a/packages/desktop-ui-lib/src/lib/screen-capture/screen-capture.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/screen-capture/screen-capture.component.spec.ts @@ -1,11 +1,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ScreenCaptureComponent } from './screen-capture.component'; +import { environment } from '@gauzy/ui-config'; +import { GAUZY_ENV } from '../constants'; describe('ScreenCaptureComponent', () => { let component: ScreenCaptureComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ScreenCaptureComponent] + imports: [ScreenCaptureComponent], + // The desktop apps provide this library's GAUZY_ENV token as the web environment plus their own + // flags (apps/desktop/src/main.ts). The web environment is enough for a render. + providers: [{ provide: GAUZY_ENV, useValue: environment }] }).compileComponents(); }); beforeEach(() => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-status/plugin-status.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-status/plugin-status.component.spec.ts index 1d2886c6dd..ae6c1de2c3 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-status/plugin-status.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-status/plugin-status.component.spec.ts @@ -9,6 +9,8 @@ describe('PluginStatusComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(PluginStatusComponent); component = fixture.componentInstance; + // A smart-table cell renderer: the table sets `rowData` before the first render. + component.rowData = { isActivate: true }; fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-update/plugin-update.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-update/plugin-update.component.spec.ts index 5c111ed514..369cd7f36c 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-update/plugin-update.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-list/plugin-update/plugin-update.component.spec.ts @@ -9,6 +9,8 @@ describe('PluginUpdateComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(PluginUpdateComponent); component = fixture.componentInstance; + // A smart-table cell renderer: the table sets `rowData` before the first render. + component.rowData = { updatedAt: new Date('2026-01-05T09:00:00.000Z') }; fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-basic-information/plugin-basic-information.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-basic-information/plugin-basic-information.component.spec.ts index 828c56c537..20060b573f 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-basic-information/plugin-basic-information.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-basic-information/plugin-basic-information.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PluginBasicInformationComponent } from './plugin-basic-information.component'; +import { FormControl, FormGroup } from '@angular/forms'; describe('PluginBasicInformationComponent', () => { let component: PluginBasicInformationComponent; let fixture: ComponentFixture; @@ -9,6 +10,14 @@ describe('PluginBasicInformationComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(PluginBasicInformationComponent); component = fixture.componentInstance; + // A section of the upload form: the parent passes its FormGroup in (see plugin-marketplace-upload). + component.form = new FormGroup({ + name: new FormControl(''), + description: new FormControl(''), + type: new FormControl(null), + status: new FormControl(null), + categoryId: new FormControl(null) + }); fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-metadata/plugin-metadata.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-metadata/plugin-metadata.component.spec.ts index b2ae396734..78cd2586fe 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-metadata/plugin-metadata.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-metadata/plugin-metadata.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PluginMetadataComponent } from './plugin-metadata.component'; +import { FormControl, FormGroup } from '@angular/forms'; describe('PluginMetadataComponent', () => { let component: PluginMetadataComponent; let fixture: ComponentFixture; @@ -9,6 +10,13 @@ describe('PluginMetadataComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(PluginMetadataComponent); component = fixture.componentInstance; + // A section of the upload form: the parent passes its FormGroup in (see plugin-marketplace-upload). + component.form = new FormGroup({ + author: new FormControl(''), + license: new FormControl(''), + homepage: new FormControl(''), + repository: new FormControl('') + }); fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-version/plugin-version.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-version/plugin-version.component.spec.ts index 516d5ebce7..9dcdff94c5 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-version/plugin-version.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/component/plugin-marketplace/plugin-marketplace-upload/plugin-version/plugin-version.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PluginVersionComponent } from './plugin-version.component'; +import { FormArray, FormControl, FormGroup } from '@angular/forms'; describe('PluginVersionComponent', () => { let component: PluginVersionComponent; let fixture: ComponentFixture; @@ -9,6 +10,13 @@ describe('PluginVersionComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(PluginVersionComponent); component = fixture.componentInstance; + // A section of the upload form: the parent passes its version FormGroup in (see plugin-marketplace-upload). + component.form = new FormGroup({ + number: new FormControl(''), + changelog: new FormControl(''), + releaseDate: new FormControl(new Date('2026-01-05T00:00:00.000Z')), + sources: new FormArray([]) + }); fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/plugins/shared/ui/source-container/source-container.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/plugins/shared/ui/source-container/source-container.component.spec.ts index 7a7b1151f5..0eb616797b 100644 --- a/packages/desktop-ui-lib/src/lib/settings/plugins/shared/ui/source-container/source-container.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/plugins/shared/ui/source-container/source-container.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SourceContainerComponent } from './source-container.component'; +import { FormArray } from '@angular/forms'; describe('SourceContainerComponent', () => { let component: SourceContainerComponent; let fixture: ComponentFixture; @@ -9,6 +10,8 @@ describe('SourceContainerComponent', () => { }).compileComponents(); fixture = TestBed.createComponent(SourceContainerComponent); component = fixture.componentInstance; + // The parent form passes its `sources` FormArray in; the template iterates its controls. + component.sources = new FormArray([]); fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/desktop-ui-lib/src/lib/settings/settings.component.spec.ts b/packages/desktop-ui-lib/src/lib/settings/settings.component.spec.ts index fe81bfdd62..ca10394c6f 100644 --- a/packages/desktop-ui-lib/src/lib/settings/settings.component.spec.ts +++ b/packages/desktop-ui-lib/src/lib/settings/settings.component.spec.ts @@ -1,11 +1,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SettingsComponent } from './settings.component'; +import { environment } from '@gauzy/ui-config'; +import { GAUZY_ENV } from '../constants'; describe('SettingsComponent', () => { let component: SettingsComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [SettingsComponent] + imports: [SettingsComponent], + // The desktop apps provide this library's GAUZY_ENV token as the web environment plus their own + // flags (apps/desktop/src/main.ts). The web environment is enough for a render. + providers: [{ provide: GAUZY_ENV, useValue: environment }] }).compileComponents(); }); beforeEach(() => { diff --git a/packages/desktop-ui-lib/src/test-setup.ts b/packages/desktop-ui-lib/src/test-setup.ts index 0ec3bbfb69..28fcf6779e 100644 --- a/packages/desktop-ui-lib/src/test-setup.ts +++ b/packages/desktop-ui-lib/src/test-setup.ts @@ -8,3 +8,42 @@ globalThis.ngJest = { import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; setupZoneTestEnv(); + +/** + * The desktop apps render this library behind a preload script that exposes `window.electronAPI` + * (see apps/agent/src/main/preload/contextBridge.ts): IPC, `remote.app`, `shell` and electron-log. + * `ElectronService` and `LoggerService` read everything from it, so without one every component that + * touches IPC or the logger failed at construction ("reading 'on'", "reading 'error'", "reading + * 'app'") before its spec could assert anything. This is an inert bridge of the same shape: IPC calls + * go nowhere (`invoke` resolves to undefined), and the log forwards to the real console so test + * output stays readable. + */ +const noop = (): void => undefined; +const testConsole = { + log: console.log.bind(console), + info: console.info.bind(console), + warn: console.warn.bind(console), + error: console.error.bind(console), + debug: console.debug.bind(console) +}; +(window as any).electronAPI = { + ipcRenderer: { + send: noop, + invoke: async (): Promise => undefined, + on: noop, + once: noop, + removeListener: noop, + removeAllListeners: noop + }, + getGlobal: (): Record => ({}), + shell: { openExternal: async (): Promise => undefined }, + remote: { + app: { + getLocale: (): string => 'en', + getName: (): string => 'gauzy-desktop-test', + getVersion: (): string => '0.0.0', + quit: noop + } + }, + log: { ...testConsole, functions: testConsole } +}; diff --git a/packages/plugins/integration-ai-ui/jest.config.ts b/packages/plugins/integration-ai-ui/jest.config.ts index a497608016..ecd6820e3f 100644 --- a/packages/plugins/integration-ai-ui/jest.config.ts +++ b/packages/plugins/integration-ai-ui/jest.config.ts @@ -1,7 +1,8 @@ export default { displayName: 'plugin-integration-ai-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/integration-ai-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/integration-github-ui/jest.config.ts b/packages/plugins/integration-github-ui/jest.config.ts index a448be0674..9070319660 100644 --- a/packages/plugins/integration-github-ui/jest.config.ts +++ b/packages/plugins/integration-github-ui/jest.config.ts @@ -1,7 +1,8 @@ export default { displayName: 'plugin-integration-github-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/integration-github-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/integration-hubstaff-ui/jest.config.ts b/packages/plugins/integration-hubstaff-ui/jest.config.ts index ac54b1fcbe..da98d7f864 100644 --- a/packages/plugins/integration-hubstaff-ui/jest.config.ts +++ b/packages/plugins/integration-hubstaff-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-integration-hubstaff-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/integration-hubstaff-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/integration-upwork-ui/jest.config.ts b/packages/plugins/integration-upwork-ui/jest.config.ts index 336f8a2c44..186adf352f 100644 --- a/packages/plugins/integration-upwork-ui/jest.config.ts +++ b/packages/plugins/integration-upwork-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-integration-upwork-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/integration-upwork-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/job-employee-ui/jest.config.ts b/packages/plugins/job-employee-ui/jest.config.ts index 84576508ee..6f0e35d3f0 100644 --- a/packages/plugins/job-employee-ui/jest.config.ts +++ b/packages/plugins/job-employee-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-job-employee-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/job-employee-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/job-matching-ui/jest.config.ts b/packages/plugins/job-matching-ui/jest.config.ts index e0229b10ab..0f049dad2e 100644 --- a/packages/plugins/job-matching-ui/jest.config.ts +++ b/packages/plugins/job-matching-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-job-matching-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/job-matching-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/job-proposal-ui/jest.config.ts b/packages/plugins/job-proposal-ui/jest.config.ts index 5364b60f74..d7cbf87ab5 100644 --- a/packages/plugins/job-proposal-ui/jest.config.ts +++ b/packages/plugins/job-proposal-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-job-proposal-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/job-proposal-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-form/proposal-template-form.component.spec.ts b/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-form/proposal-template-form.component.spec.ts index 6f2eeec27a..ae1305782c 100644 --- a/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-form/proposal-template-form.component.spec.ts +++ b/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-form/proposal-template-form.component.spec.ts @@ -1,11 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProposalTemplateFormComponent } from './proposal-template-form.component'; +import { JobProposalTemplateModule } from '../../job-proposal-template.module'; describe('ProposalTemplateFormComponent', () => { let component: ProposalTemplateFormComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ProposalTemplateFormComponent], + // Not standalone: import its NgModule for the template's real scope. + imports: [JobProposalTemplateModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-list/proposal-template-list.component.spec.ts b/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-list/proposal-template-list.component.spec.ts index f2a8c52fa5..14f2fad110 100644 --- a/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-list/proposal-template-list.component.spec.ts +++ b/packages/plugins/job-proposal-ui/src/lib/proposal-template/components/proposal-template-list/proposal-template-list.component.spec.ts @@ -1,11 +1,14 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProposalTemplateListComponent } from './proposal-template-list.component'; +import { JobProposalTemplateModule } from '../../job-proposal-template.module'; +import { PipesModule } from '@gauzy/ui-core/shared'; describe('ProposalTemplateListComponent', () => { let component: ProposalTemplateListComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ProposalTemplateListComponent], + // Not standalone: import its NgModule. PipesModule provides the Nl2Br/Truncate pipes it injects (the app gets them from its root imports). + imports: [JobProposalTemplateModule, PipesModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/plugins/job-search-ui/jest.config.ts b/packages/plugins/job-search-ui/jest.config.ts index 1ea882f723..fbab599ac8 100644 --- a/packages/plugins/job-search-ui/jest.config.ts +++ b/packages/plugins/job-search-ui/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'plugin-job-search-ui', preset: '../../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../../jest.angular-defaults.ts'], coverageDirectory: '../../../coverage/packages/plugins/job-search-ui', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/ui-auth/jest.config.ts b/packages/ui-auth/jest.config.ts index 85d6d7e14a..5a89ea75e9 100644 --- a/packages/ui-auth/jest.config.ts +++ b/packages/ui-auth/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'ui-auth', preset: '../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../jest.angular-defaults.ts'], coverageDirectory: '../../coverage/packages/ui-auth', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/ui-auth/src/lib/components/login-magic/login-magic.component.spec.ts b/packages/ui-auth/src/lib/components/login-magic/login-magic.component.spec.ts index 5c86f786c4..7235d5b934 100644 --- a/packages/ui-auth/src/lib/components/login-magic/login-magic.component.spec.ts +++ b/packages/ui-auth/src/lib/components/login-magic/login-magic.component.spec.ts @@ -1,11 +1,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NbAuthModule } from '@nebular/auth'; import { NgxLoginMagicComponent } from './login-magic.component'; +import { NgxAuthModule } from '../../auth.module'; describe('NgxLoginMagicComponent', () => { let component: NgxLoginMagicComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [NgxLoginMagicComponent] + // Declared in NgxAuthModule (not standalone): import it for the template's real scope + // (ngx-gauzy-logo, Nebular forms, ...). NbAuthService comes from NbAuthModule.forRoot(), which the + // app's root module calls. + imports: [NgxAuthModule, NbAuthModule.forRoot()] }).compileComponents(); }); beforeEach(() => { diff --git a/packages/ui-auth/src/lib/components/magic-login-workspace/magic-login-workspace.component.spec.ts b/packages/ui-auth/src/lib/components/magic-login-workspace/magic-login-workspace.component.spec.ts index a796a096bf..8b3fad63fc 100644 --- a/packages/ui-auth/src/lib/components/magic-login-workspace/magic-login-workspace.component.spec.ts +++ b/packages/ui-auth/src/lib/components/magic-login-workspace/magic-login-workspace.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NbAuthModule } from '@nebular/auth'; import { NgxMagicSignInWorkspaceComponent } from './magic-login-workspace.component'; +import { NgxAuthModule } from '../../auth.module'; describe('NgxMagicSignInWorkspaceComponent', () => { let component: NgxMagicSignInWorkspaceComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [NgxMagicSignInWorkspaceComponent] + // Declared in NgxAuthModule (not standalone): import it for the template's real scope and the + // AuthService it provides. NbAuthModule.forRoot() is the app root module's call. + imports: [NgxAuthModule, NbAuthModule.forRoot()] }).compileComponents(); }); beforeEach(() => { diff --git a/packages/ui-core/jest.config.ts b/packages/ui-core/jest.config.ts index 76611c643c..4fa1aaad31 100644 --- a/packages/ui-core/jest.config.ts +++ b/packages/ui-core/jest.config.ts @@ -2,7 +2,8 @@ export default { displayName: 'ui-core', preset: '../../jest.preset.js', - setupFilesAfterEnv: ['/src/test-setup.ts'], + // test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file). + setupFilesAfterEnv: ['/src/test-setup.ts', '/../../jest.angular-defaults.ts'], coverageDirectory: '../../coverage/packages/ui-core', transform: { '^.+\\.(ts|mjs|js|html)$': [ diff --git a/packages/ui-core/shared/src/lib/dialogs/prompt/prompt.component.spec.ts b/packages/ui-core/shared/src/lib/dialogs/prompt/prompt.component.spec.ts index 818bc9184b..7b9f19c6ac 100644 --- a/packages/ui-core/shared/src/lib/dialogs/prompt/prompt.component.spec.ts +++ b/packages/ui-core/shared/src/lib/dialogs/prompt/prompt.component.spec.ts @@ -1,11 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PromptComponent } from './prompt.component'; +import { DialogsModule } from '../dialogs.module'; describe('PromptComponent', () => { let component: PromptComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [PromptComponent], + // Declared in DialogsModule (not standalone): import it so the template gets that module's scope. + imports: [DialogsModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/ui-core/shared/src/lib/dialogs/quick-actions/quick-actions.component.spec.ts b/packages/ui-core/shared/src/lib/dialogs/quick-actions/quick-actions.component.spec.ts index 0b239e9963..c7cf392266 100644 --- a/packages/ui-core/shared/src/lib/dialogs/quick-actions/quick-actions.component.spec.ts +++ b/packages/ui-core/shared/src/lib/dialogs/quick-actions/quick-actions.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { QuickActionsComponent } from './quick-actions.component'; +import { DialogsModule } from '../dialogs.module'; +import { TimeTrackerService } from '@gauzy/ui-core/core'; describe('QuickActionsComponent', () => { let component: QuickActionsComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [QuickActionsComponent] + // Declared in DialogsModule (not standalone): import it so the template gets that module's scope. The real TimeTrackerService starts a Web Worker, which jsdom lacks; the menu only reads `running`. + imports: [DialogsModule], + providers: [{ provide: TimeTrackerService, useValue: { running: false } }] }).compileComponents(); }); beforeEach(() => { diff --git a/packages/ui-core/shared/src/lib/gallery/gallery.component.spec.ts b/packages/ui-core/shared/src/lib/gallery/gallery.component.spec.ts index e33ded8ff4..0077553a42 100644 --- a/packages/ui-core/shared/src/lib/gallery/gallery.component.spec.ts +++ b/packages/ui-core/shared/src/lib/gallery/gallery.component.spec.ts @@ -1,11 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { GalleryComponent } from './gallery.component'; +import { GalleryModule } from './gallery.module'; describe('GalleryComponent', () => { let component: GalleryComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [GalleryComponent], + // Not standalone: import its NgModule so the template gets its pipes (utcToTimezone, ...). + imports: [GalleryModule], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/ui-core/shared/src/lib/project-module/project-module-table/project-module-table.component.spec.ts b/packages/ui-core/shared/src/lib/project-module/project-module-table/project-module-table.component.spec.ts index bea1293b7e..cf34499d7e 100644 --- a/packages/ui-core/shared/src/lib/project-module/project-module-table/project-module-table.component.spec.ts +++ b/packages/ui-core/shared/src/lib/project-module/project-module-table/project-module-table.component.spec.ts @@ -1,11 +1,13 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ProjectModuleTableComponent } from './project-module-table.component'; +import { ProjectModuleTableModule } from './project-module-table.module'; describe('ProjectModuleTableComponent', () => { let component: ProjectModuleTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [ProjectModuleTableComponent] + // Not standalone (standalone: false), so it cannot be imported directly; import the NgModule that declares it. + imports: [ProjectModuleTableModule] }).compileComponents(); fixture = TestBed.createComponent(ProjectModuleTableComponent); component = fixture.componentInstance; diff --git a/packages/ui-core/shared/src/lib/report/daily-statistics/daily-statistics/daily-statistics.component.spec.ts b/packages/ui-core/shared/src/lib/report/daily-statistics/daily-statistics/daily-statistics.component.spec.ts index a428a8282b..64af4aa22a 100644 --- a/packages/ui-core/shared/src/lib/report/daily-statistics/daily-statistics/daily-statistics.component.spec.ts +++ b/packages/ui-core/shared/src/lib/report/daily-statistics/daily-statistics/daily-statistics.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DailyStatisticsComponent } from './daily-statistics.component'; +import { DailyStatisticsModule } from '../daily-statistics.module'; +import { EmployeesService } from '@gauzy/ui-core/core'; describe('DailyStatisticsComponent', () => { let component: DailyStatisticsComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [DailyStatisticsComponent], + // Not standalone: import its NgModule. EmployeesService is provided by the host feature module in the app. + imports: [DailyStatisticsModule], + providers: [EmployeesService], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/ui-core/shared/src/lib/tasks/add-task-dialog/add-task-dialog.component.spec.ts b/packages/ui-core/shared/src/lib/tasks/add-task-dialog/add-task-dialog.component.spec.ts index 2115f5a6f3..424dbfa53a 100644 --- a/packages/ui-core/shared/src/lib/tasks/add-task-dialog/add-task-dialog.component.spec.ts +++ b/packages/ui-core/shared/src/lib/tasks/add-task-dialog/add-task-dialog.component.spec.ts @@ -1,11 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AddTaskDialogComponent } from './add-task-dialog.component'; +import { AddTaskDialogModule } from './add-task-dialog.module'; +import { AuthService, EmployeesService } from '@gauzy/ui-core/core'; describe('AddTaskDialogComponent', () => { let component: AddTaskDialogComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [AddTaskDialogComponent], + // Not standalone: import its NgModule. EmployeesService and AuthService are provided by the host feature modules in the app. + imports: [AddTaskDialogModule], + providers: [EmployeesService, AuthService], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/ui-core/shared/src/lib/time-tracker/time-tracker/time-tracker.component.spec.ts b/packages/ui-core/shared/src/lib/time-tracker/time-tracker/time-tracker.component.spec.ts index 75d99ee483..752dbd77c6 100644 --- a/packages/ui-core/shared/src/lib/time-tracker/time-tracker/time-tracker.component.spec.ts +++ b/packages/ui-core/shared/src/lib/time-tracker/time-tracker/time-tracker.component.spec.ts @@ -1,11 +1,32 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimeTrackerComponent } from './time-tracker.component'; +import { TimeTrackerModule } from '../time-tracker.module'; + +/** + * TimeTrackerService runs its clock in a Web Worker when `Worker` exists and skips it otherwise, but + * its `ngOnDestroy` terminates the worker unconditionally. jsdom has no `Worker`, so TestBed teardown + * threw. Browsers always have one; give the test environment the same, as an inert stand-in. + */ +class WorkerStub { + onmessage: ((event: MessageEvent) => void) | null = null; + postMessage(): void {} + terminate(): void {} +} + describe('TimeTrackerComponent', () => { let component: TimeTrackerComponent; let fixture: ComponentFixture; + const originalWorker = (globalThis as any).Worker; + beforeAll(() => { + (globalThis as any).Worker = WorkerStub; + }); + afterAll(() => { + (globalThis as any).Worker = originalWorker; + }); beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TimeTrackerComponent], + // Not standalone: import its NgModule; `forRoot()` also provides TimeTrackerService, as the app does. + imports: [TimeTrackerModule.forRoot()], teardown: { destroyAfterEach: false } }).compileComponents(); }); diff --git a/packages/ui-core/shared/src/lib/timesheet/edit-time-log-modal/edit-time-log-modal.component.spec.ts b/packages/ui-core/shared/src/lib/timesheet/edit-time-log-modal/edit-time-log-modal.component.spec.ts index 8f7c173a9c..e01ecfd56d 100644 --- a/packages/ui-core/shared/src/lib/timesheet/edit-time-log-modal/edit-time-log-modal.component.spec.ts +++ b/packages/ui-core/shared/src/lib/timesheet/edit-time-log-modal/edit-time-log-modal.component.spec.ts @@ -1,17 +1,29 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { EditTimeLogModalComponent } from './edit-time-log-modal.component'; +import { EditTimeLogModalModule } from './edit-time-log-modal.module'; +import { AuthService } from '@gauzy/ui-core/core'; describe('EditTimeLogModalComponent', () => { let component: EditTimeLogModalComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [EditTimeLogModalComponent], + // Not standalone: import its NgModule so the template gets the forms / selector modules it uses. + imports: [EditTimeLogModalModule], + // Provided by PagesModule for every authenticated page in the app. + providers: [AuthService], teardown: { destroyAfterEach: false } }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(EditTimeLogModalComponent); component = fixture.componentInstance; + // The app always opens this modal with a `timeLog` context. With none, the form falls back to the + // store's selected employee, and no employee is selected in a bare test store. + component.timeLog = { + employeeId: 'employee-1', + startedAt: new Date('2026-01-05T09:00:00.000Z'), + stoppedAt: new Date('2026-01-05T10:00:00.000Z') + }; fixture.detectChanges(); }); it('should create', () => { diff --git a/packages/ui-core/shared/src/lib/timesheet/gauzy-filters/gauzy-filters.component.spec.ts b/packages/ui-core/shared/src/lib/timesheet/gauzy-filters/gauzy-filters.component.spec.ts index 81d7faa810..3bfec63716 100644 --- a/packages/ui-core/shared/src/lib/timesheet/gauzy-filters/gauzy-filters.component.spec.ts +++ b/packages/ui-core/shared/src/lib/timesheet/gauzy-filters/gauzy-filters.component.spec.ts @@ -1,15 +1,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { GauzyRangePickerComponent } from './gauzy-filters.component'; -describe('GauzyRangePickerComponent', () => { - let component: GauzyRangePickerComponent; - let fixture: ComponentFixture; +import { GauzyFiltersComponent } from './gauzy-filters.component'; +import { GauzyFiltersModule } from './gauzy-filters.module'; +describe('GauzyFiltersComponent', () => { + let component: GauzyFiltersComponent; + let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [GauzyRangePickerComponent] + // Not standalone: import its NgModule. (This stub imported `GauzyRangePickerComponent`, a class the file does not export, so it declared `undefined`.) + imports: [GauzyFiltersModule] }).compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(GauzyRangePickerComponent); + fixture = TestBed.createComponent(GauzyFiltersComponent); component = fixture.componentInstance; fixture.detectChanges(); });