Merge pull request #10324 from ever-co/fix/unit-tests-hermetic-harness

test: make the Unit Tests workflow green (97/97 projects): hermetic env, Angular jest harness, stub specs
This commit is contained in:
Ruslan Konviser
2026-09-28 10:50:02 +02:00
committed by GitHub
113 changed files with 1187 additions and 180 deletions
+18
View File
@@ -87,7 +87,25 @@ jobs:
# is no `if:` on a previous step's outcome to get wrong.
run: .github/scripts/restore-node-modules.sh
- name: Generate the Angular environment file
# `packages/ui-config/src/lib/environments/environment.ts` is generated (and gitignored), so a
# fresh checkout has none — and every spec that reaches `@gauzy/ui-config`, directly or through
# ui-core, failed to load with "Cannot find module './lib/environments/environment'" (about a
# dozen Angular projects). Same command the Playwright workflow runs before it builds the web
# app. It goes through yarn, not Nx, so `.env.local` is not loaded here either.
run: yarn config:dev
- name: Run unit tests
env:
# Nx loads the workspace `.env` files into every task by default, and this repository commits
# a `.env.local` tuned for LOCAL DEVELOPMENT — DEMO=true, WORKER_QUEUE_ENABLED=false,
# NODE_ENV=development, and so on. Every unit test therefore ran as a demo-mode dev process:
# the logs printed "Is DEMO: true" 141 times and never once "false", and specs asserting
# production behaviour (role-permission counts, the worker's BullMQ root) failed for that
# reason alone. The suites must not depend on whichever `.env` sits in the checkout, so this
# turns the loading off; a spec that needs a particular mode pins it itself. `.env.local`
# stays as it is — it is the local-dev default, and nothing here needs it removed.
NX_LOAD_DOT_ENV_FILES: 'false'
# `--nxBail=false` so one failing project still lets every other project report; without that
# the first failure hides the rest and each fix costs another full run to find the next
# problem. It is the real flag name — nx has no `--continue`, and an unknown option is
+4 -1
View File
@@ -12,7 +12,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -1
View File
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -1
View File
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+19 -1
View File
@@ -2,5 +2,23 @@
export default {
displayName: 'gauzy',
preset: '../../jest.preset.js',
coverageDirectory: '../../coverage/apps/gauzy'
// 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.
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../jest.angular-defaults.ts'],
coverageDirectory: '../../coverage/apps/gauzy',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$'
}
]
},
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment'
]
};
+1 -2
View File
@@ -182,8 +182,7 @@
"test": {
"executor": "@nx/jest:jest",
"options": {
"jestConfig": "apps/gauzy/jest.config.js",
"setupFile": "apps/gauzy/src/test-setup.ts",
"jestConfig": "apps/gauzy/jest.config.ts",
"tsConfig": "apps/gauzy/tsconfig.spec.json"
}
},
+9 -2
View File
@@ -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', () => {
@@ -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<AppUrlActivityComponent>;
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();
@@ -1,15 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ScreenshotComponent } from './screenshot.component';
import { ScreenshotModule } from '../screenshot.module';
import { DateRangePickerBuilderService, DEFAULT_DATE_PICKER_CONFIG, EmployeesService } from '@gauzy/ui-core/core';
describe('ScreenshotComponent', () => {
let component: ScreenshotComponent;
let fixture: ComponentFixture<ScreenshotComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ScreenshotComponent],
// Not standalone: import the NgModule that declares it, for the template's real scope.
imports: [ScreenshotModule],
// Provided by the host feature module in the app (e.g. the employees/reports parents).
providers: [EmployeesService],
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();
@@ -1,15 +1,23 @@
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, EmployeesService } from '@gauzy/ui-core/core';
describe('TimeActivitiesComponent', () => {
let component: TimeActivitiesComponent;
let fixture: ComponentFixture<TimeActivitiesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TimeActivitiesComponent],
// Not standalone: import the NgModule that declares it, for the template's real scope.
imports: [TimeAndActivitiesModule],
// Provided by the host feature module in the app (e.g. the employees/reports parents).
providers: [EmployeesService],
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();
@@ -18,4 +26,3 @@ describe('TimeActivitiesComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -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<CalendarComponent>;
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();
@@ -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<DailyComponent>;
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();
@@ -1,11 +1,28 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TimesheetLayoutComponent } from './layout.component';
import { TimesheetModule } from '../timesheet.module';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
describe('TimesheetLayoutComponent', () => {
let component: TimesheetLayoutComponent;
let fixture: ComponentFixture<TimesheetLayoutComponent>;
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,
// The component reads `snapshot.data`; the nb-tabset it renders subscribes to `params`.
useValue: {
snapshot: { data: { tabsetId: 'timesheet-page' }, params: {}, queryParams: {} },
data: of({ tabsetId: 'timesheet-page' }),
params: of({}),
queryParams: of({})
}
}
],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,16 +1,19 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewComponent } from './view.component';
describe('ViewComponent', () => {
let component: ViewComponent;
let fixture: ComponentFixture<ViewComponent>;
import { TimesheetViewComponent } from './view.component';
import { ViewModule } from '../view.module';
describe('TimesheetViewComponent', () => {
let component: TimesheetViewComponent;
let fixture: ComponentFixture<TimesheetViewComponent>;
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();
});
@@ -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<WeeklyComponent>;
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();
@@ -5,7 +5,8 @@ describe('GoalsComponent', () => {
let fixture: ComponentFixture<GoalsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GoalsComponent],
// Standalone: it is imported, not declared.
imports: [GoalsComponent],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -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<AmountsOwedReportComponent>;
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();
@@ -18,4 +24,3 @@ describe('AmountsOwedReportComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -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<AppsUrlsReportComponent>;
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();
@@ -18,4 +24,3 @@ describe('AppsUrlsReportComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -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<ExpensesReportComponent>;
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();
@@ -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<ManualTimeComponent>;
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();
@@ -18,4 +24,3 @@ describe('ManualTimeComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -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<PaymentReportComponent>;
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();
@@ -18,4 +24,3 @@ describe('PaymentReportComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -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<TimeLimitReportComponent>;
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();
@@ -18,4 +24,3 @@ describe('TimeLimitReportComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -1,15 +1,23 @@
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, EmployeesService } from '@gauzy/ui-core/core';
describe('TimeReportsComponent', () => {
let component: TimeReportsComponent;
let fixture: ComponentFixture<TimeReportsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [TimeReportsComponent],
// Not standalone: import the NgModule that declares it, for the template's real scope.
imports: [TimeReportsModule],
// Provided by the host feature module in the app (e.g. the employees/reports parents).
providers: [EmployeesService],
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();
@@ -18,4 +26,3 @@ describe('TimeReportsComponent', () => {
expect(component).toBeTruthy();
});
});
@@ -1,15 +1,42 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MyTaskDialogComponent } from './my-task-dialog.component';
import { TasksModule } from '../../tasks.module';
import { IOrganization, IUser } from '@gauzy/contracts';
import { EmployeesService, Store } from '@gauzy/ui-core/core';
/**
* TasksModule imports DocumentLinksPanelComponent from the docs-ui plugin, and that package's entry
* point also loads its PDF viewer, which resolves the pdf.js worker with `import.meta.url` — syntax
* the CommonJS Jest runtime cannot parse, so this suite failed to load. The panel is docs-ui's own
* (tested there); here a same-selector stand-in with the same inputs keeps the dialog's template whole.
*/
jest.mock('@gauzy/plugin-docs-ui', () => {
const { Component } = jest.requireActual('@angular/core');
class DocumentLinksPanelComponent {}
Component({
selector: 'gz-document-links-panel',
template: '',
inputs: ['entity', 'entityId', 'entityLabel', 'readonly', 'hideWhenEmpty']
})(DocumentLinksPanelComponent);
return { DocumentLinksPanelComponent };
});
describe('MyTaskDialogComponent', () => {
let component: MyTaskDialogComponent;
let fixture: ComponentFixture<MyTaskDialogComponent>;
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();
});
beforeEach(() => {
// The dialog opens inside a signed-in session with an organization selected; it reads both on init
// (an unguarded `selectedOrganization.id` in an async loader crashed the whole Jest worker).
const store = TestBed.inject(Store);
store.selectedOrganization = { id: 'organization-1', tenantId: 'tenant-1' } as IOrganization;
store.user = { id: 'user-1', tenantId: 'tenant-1', employee: { id: 'employee-1' } } as IUser;
fixture = TestBed.createComponent(MyTaskDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
+5 -1
View File
@@ -1 +1,5 @@
import 'jest-preset-angular';
// `import 'jest-preset-angular'` was the pre-v14 way to set up the Angular test environment; the
// current jest-preset-angular (16) exposes it as `setupZoneTestEnv()`, same as the other Angular apps.
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv();
+7 -1
View File
@@ -5,7 +5,13 @@
"module": "preserve",
"types": ["jest", "node", "google.maps"],
"moduleResolution": "bundler",
"isolatedModules": true
// Matches ui-core / docs-ui / videos-ui. The app default-imports CommonJS packages (e.g.
// `import AnnotationPlugin from 'chartjs-plugin-annotation'` in ui-core's line chart), which the
// bundler resolves; without interop ts-jest reads an undefined `.default` and Chart.register threw.
// The namespace-imported callables (moment, randomcolor) are covered by the root jest.interop.js.
"isolatedModules": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
},
"files": ["src/test-setup.ts"],
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
+3
View File
@@ -47,7 +47,10 @@
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/mcp-auth/jest.config.ts",
"passWithNoTests": true
}
}
+4 -1
View File
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -1
View File
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+30 -11
View File
@@ -1,11 +1,29 @@
import { TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';
import { LanguageElectronService } from '@gauzy/desktop-ui-lib';
import { AppComponent } from './app.component';
import { RouterTestingModule } from '@angular/router/testing';
/**
* The root shell of the Gauzy Server renderer: a router outlet, plus the Electron language bridge
* started on init. This spec began as the 2021 Angular CLI scaffold (a `title` of 'desktop-web-ui' and
* an h1 "Welcome to desktop-web-ui!"); the component replaced that placeholder long ago and the spec
* never ran, so those two cases now assert what the shell really does. The component is standalone
* (imported, not declared), and the language service talks to Electron IPC, so it is a stand-in here.
*
* The library entry point is mocked down to that one token: loading it for real pulls every desktop
* feature module (ngx-charts and the d3 tree among them) into a spec that uses none of them, which
* took this three-case suite ~15 minutes on a cold transform cache.
*/
jest.mock('@gauzy/desktop-ui-lib', () => ({ LanguageElectronService: class LanguageElectronService {} }));
describe('AppComponent', () => {
const languageElectronService = { initialize: jest.fn() };
beforeEach(async () => {
languageElectronService.initialize.mockClear();
await TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent]
imports: [AppComponent],
providers: [provideRouter([]), { provide: LanguageElectronService, useValue: languageElectronService }]
}).compileComponents();
});
it('should create the app', () => {
@@ -13,15 +31,16 @@ describe('AppComponent', () => {
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'desktop-web-ui'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('desktop-web-ui');
});
it('should render title', () => {
it('starts the Electron language bridge on init', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to desktop-web-ui!');
expect(languageElectronService.initialize).toHaveBeenCalledTimes(1);
});
it('renders the router outlet as the whole shell', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled: HTMLElement = fixture.nativeElement;
expect(compiled.querySelector('router-outlet')).toBeTruthy();
expect(compiled.children).toHaveLength(1);
});
});
+16
View File
@@ -38,6 +38,22 @@ jest.mock('@gauzy/scheduler', () => ({
jest.mock('./worker-jobs.module', () => ({ WorkerJobsModule: class WorkerJobsModule {} }));
/**
* `worker.constants.ts` reads `WORKER_QUEUE_ENABLED` / `WORKER_SCHEDULER_ENABLED` once, at import
* time, and the assertions below are about the worker's DEFAULT wiring. So the variables are cleared
* right before the real module is evaluated — otherwise the result depends on whichever `.env` file
* was loaded into the test process: Nx loads the committed `.env.local` (which sets both to `false`
* for local development) into every task by default, and that alone turned the BullMQ-root test red
* in CI. A mock factory is the one place that is guaranteed to run before `./app.module` pulls the
* constants in, whatever order the transpiler emits the imports in. Jest gives each test file its
* own copy of `process.env`, so this does not leak into other suites.
*/
jest.mock('./worker.constants', () => {
delete process.env.WORKER_QUEUE_ENABLED;
delete process.env.WORKER_SCHEDULER_ENABLED;
return jest.requireActual('./worker.constants');
});
// `@Module()` writes its metadata through `Reflect.defineMetadata`; nothing else in this file
// pulls the polyfill in, and without it the reads below come back undefined.
import 'reflect-metadata';
+98
View File
@@ -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<NbDialogRef<unknown>> = {
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 <nb-icon> 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 }
]
});
});
+45
View File
@@ -0,0 +1,45 @@
/**
* Jest `setupFiles` entry for the whole workspace (wired in `jest.preset.js`): make the callable
* CommonJS packages that this codebase imports BOTH ways load under either import style, the way the
* app bundlers already treat them.
*
* `moment` is a CommonJS function (`module.exports = moment`, no `default`, no `__esModule`), and the
* code imports it two ways:
* - `import moment from 'moment'` — ui-core (43 files), public-layout-ui, the React UI plugins;
* - `import * as moment from 'moment'` — core, desktop-lib, desktop-ui-lib, apps/gauzy and others.
* Webpack / esbuild accept both. The TypeScript CommonJS emit Jest runs cannot accept both under one
* `esModuleInterop` setting:
* - `esModuleInterop: false` (the root tsconfig) compiles the default import to `moment_1.default`,
* which is undefined — "(0, moment_1.default) is not a function" failed ~50 Angular suites at load
* time, in every project that reaches ui-core's date-range-picker service;
* - `esModuleInterop: true` fixes that, but compiles the namespace import through `__importStar`,
* which copies the module's properties into a plain object — so `moment()` stops being callable.
* `randomcolor` has the same shape and is namespace-imported and called (ui-core's tags colour input,
* apps/gauzy's weekly report), which breaks in exactly the projects that set `esModuleInterop: true`.
*
* Giving each loaded module `default` (itself) and `__esModule` makes every combination resolve to the
* function itself, exactly as the bundlers do. `moment-timezone` returns the same object as `moment`.
*
* Scope: this file runs in each test file's module registry before the spec loads, so the specs see
* the patched instances. A spec that calls `jest.resetModules()` and then re-requires one of these
* gets a fresh, unpatched copy — repeat the patch there if one ever needs it.
*/
const CALLABLE_COMMONJS_PACKAGES = ['moment', 'randomcolor'];
for (const name of CALLABLE_COMMONJS_PACKAGES) {
let mod;
try {
mod = require(name);
} catch {
continue; // not installed in this checkout: nothing can import it either
}
if (typeof mod !== 'function') {
continue;
}
if (mod.default === undefined) {
mod.default = mod;
}
if (!mod.__esModule) {
Object.defineProperty(mod, '__esModule', { value: true });
}
}
+33 -4
View File
@@ -16,6 +16,13 @@ const nxPreset = require('@nx/jest/preset').default;
// camelcase -> time-tracking suites
// @faker-js/faker -> reached through the entity graph (core/seeds)
// @nestjs/axios -> ships a raw `index.ts` that re-exports `./dist`
// @datorama/akita, @ngneat/* -> the Angular state stores (ui-core and every UI plugin reach them)
// lodash-es -> Angular UI code, and angular2-smart-table's .mjs bundle
// d3-*, internmap -> @swimlane/ngx-charts (desktop-ui-lib's recap module, which the
// desktop apps' shells load through the library's entry point)
// plus every `.mjs` file: Angular and its ecosystem ship ESM-only `fesm2022/*.mjs` bundles, which the
// Angular projects compile with jest-preset-angular. A Node project only pays for that alternative
// when one of its specs actually reaches an `.mjs` file, which would otherwise fail to load anyway.
// Listing packages, rather than transforming all of node_modules, bounds the transform work. The
// `(?:.*/)?` prefix also matches nested copies and same-named directories inside other packages
// (e.g. `@smithy/uuid`); that only costs some transform time.
@@ -25,13 +32,35 @@ const nxPreset = require('@nx/jest/preset').default;
// whenever Jest hands it over, whatever `allowJs` says — so a project needs nothing else for this
// list to take effect. `allowJs` only matters for repo-local `.js` files.
// - A project that sets its own `transformIgnorePatterns` REPLACES this one rather than adding to
// it: the Angular projects (for jest-preset-angular's `.mjs` handling), `docs` (a narrower list)
// and `integration-zapier` (a copy of this list, which has to track it).
// it. The Angular projects used to do exactly that with `['node_modules/(?!.*\\.mjs$)']` — the
// `.mjs` exception and nothing else — so akita, @ngneat/effects and lodash-es never got
// transformed there and ~22 Angular suites failed to load. They now inherit this list, which is
// why the `.mjs` alternative lives here. Still defining their own: `docs` (a narrower list),
// `integration-zapier` (a copy of this list, which has to track it) and the three React
// projects (`.mjs` only, which is all they need today).
// - `@gauzy/*` is deliberately absent, and adding it would do nothing. Jest resolves workspace
// packages to their real source path (`packages/<pkg>/src/index.ts`), which has no
// `node_modules/` segment, so this pattern is never consulted for them.
const transformIgnorePatterns = [
'node_modules/(?!(?:.*/)?(sanitize-html|htmlparser2|domelementtype|domhandler|domutils|dom-serializer|entities|nanoid|parse-srcset|uuid|camelcase|@faker-js|@nestjs/axios)/)'
String.raw`node_modules/(?!.*\.mjs$|(?:.*/)?(sanitize-html|htmlparser2|domelementtype|domhandler|domutils|dom-serializer|entities|nanoid|parse-srcset|uuid|camelcase|@faker-js|@nestjs/axios|@datorama|@ngneat|lodash-es|d3-[a-z-]+|internmap)/)`
];
module.exports = { ...nxPreset, transformIgnorePatterns };
module.exports = {
...nxPreset,
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` and `randomcolor` load with both default and namespace imports, 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')]
};
+35
View File
@@ -0,0 +1,35 @@
/**
* Jest resolver for the whole workspace (wired in `jest.preset.js`): Nx's resolver, plus ONE rule —
* every `@angular/*` import inside a Jest project resolves to the SAME copy of Angular.
*
* Why: the root `package.json` sets `workspaces.nohoist: ["**\/@angular*\/**"]`, so every workspace
* that depends on Angular gets its own `node_modules/@angular/*`, next to the root copy. Node-style
* resolution then hands different files different copies:
* - a spec (and the component it tests) gets the project's copy, e.g.
* `packages/plugins/jobs-ui/node_modules/@angular/core`;
* - `jest-preset-angular/setup-env/zone`, which lives in the ROOT `node_modules`, gets the root copy;
* - a workspace library the spec pulls in (`packages/ui-core/src/...`) gets ui-core's copy.
* Each copy has its own `TestBed` and its own injection tokens. `setupZoneTestEnv()` initialised the
* root copy's TestBed while the spec used another one, so every TestBed spec failed with "Need to
* call TestBed.initTestEnvironment() first" — in desktop, desktop-ui-lib, ui-core, plugin-jobs-ui,
* gauzy-server and gauzy-api-server, on every run since the Unit Tests workflow was added.
*
* The fix resolves `@angular/*` from the Jest project's `rootDir` whoever asks: the project's own
* copy when it has one (the one it builds against), otherwise the next copy up the tree (the root
* one). One project, one Angular. Anything else goes through Nx's resolver unchanged.
*/
const nxResolver = require('@nx/jest/plugins/resolver');
const ANGULAR_SCOPE = '@angular/';
module.exports = function resolve(request, options) {
if (request.startsWith(ANGULAR_SCOPE) && options.rootDir && options.basedir !== options.rootDir) {
try {
return nxResolver(request, { ...options, basedir: options.rootDir });
} catch {
// Not resolvable from the project root (no copy on that path) — fall back to the usual
// lookup from the requiring file rather than failing a lookup that used to work.
}
}
return nxResolver(request, options);
};
+4 -1
View File
@@ -179,7 +179,10 @@
"inputs": [
"default",
"^production",
"{workspaceRoot}/jest.preset.js"
"{workspaceRoot}/jest.preset.js",
"{workspaceRoot}/jest.resolver.js",
"{workspaceRoot}/jest.interop.js",
"{workspaceRoot}/jest.angular-defaults.ts"
],
"options": {
"passWithNoTests": true
@@ -1,4 +1,5 @@
import { DataSource, EntitySchema, QueryRunner } from 'typeorm';
import { environment } from '@gauzy/config';
import { PermissionsEnum, RolesEnum } from '@gauzy/contracts';
import { RolePermissionUtils } from './utils';
import { DEFAULT_ROLE_PERMISSIONS } from './default-role-permissions';
@@ -70,9 +71,41 @@ const SEEDED_ROLES: RolesEnum[] = [
const TENANT_COUNT = 3;
const EXPECTED_ROLE_COUNT = TENANT_COUNT * SEEDED_ROLES.length;
/** Every permission the migration considers — the same list the production code walks. */
/**
* Every permission the migration considers outside demo mode — the same list the production code
* walks. The suites below pin `environment.demo` to `false` so this holds whatever the process
* environment says; the demo-mode list is covered by its own suite at the end of the file.
*/
const ALL_PERMISSIONS = Object.values(PermissionsEnum);
/**
* What `getPermissions()` leaves out in demo mode. Kept in step with `utils.ts` on purpose: if the
* production list changes, the demo-mode suite below fails instead of silently asserting a stale set.
*/
const DEMO_EXCLUDED_PERMISSIONS: PermissionsEnum[] = [
PermissionsEnum.ACCESS_DELETE_ACCOUNT,
PermissionsEnum.ACCESS_DELETE_ALL_DATA
];
/**
* `environment.demo` is read from `process.env.DEMO` once, when `@gauzy/config` loads — so without
* pinning it, the expected row counts here depend on whichever `.env` file happened to be loaded
* into the test process. Nx loads the committed `.env.local` (DEMO=true) into every task by default,
* which is why this suite failed in CI with 5016 rows (24 roles x 209) against 5064 (24 x 211) while
* passing on a machine without that file. Assigned directly rather than with `jest.replaceProperty`
* because the tests below call `jest.restoreAllMocks()`, which would un-pin it half-way through.
*/
let savedEnvironmentDemo: boolean;
beforeAll(() => {
savedEnvironmentDemo = environment.demo;
environment.demo = false;
});
afterAll(() => {
environment.demo = savedEnvironmentDemo;
});
interface Recorded {
sql: string;
parameters: any[];
@@ -332,4 +365,60 @@ describe('RolePermissionUtils.migrateRolePermissions', () => {
await dataSource.destroy();
});
});
describe('demo mode', () => {
/**
* In demo mode the reload INSERTS every missing permission EXCEPT the account- and data-deletion
* pair. This is the case CI used to run by accident (through `.env.local`); here it runs on
* purpose, with its own expected count.
*
* Scope, stated so nobody reads more into it: the reload only ever adds rows, so it never removes
* a deletion grant a tenant already holds, and these tests start from an empty `role_permission`
* table. Demo tenants are kept free of those grants by the seed (`role-permission.seed.ts`) and
* `RolePermissionService.updateRolesAndPermissions`, both of which skip the same pair in demo mode.
* `PermissionGuard` itself has no demo-mode rule.
*/
let dataSource: DataSource;
beforeAll(async () => {
environment.demo = true;
const built = await createSeededDataSource();
dataSource = built.dataSource;
await RolePermissionUtils.migrateRolePermissions(built.queryRunner);
});
afterAll(async () => {
environment.demo = false;
jest.restoreAllMocks();
await dataSource.destroy();
});
it('grants every role all permissions except the two deletion permissions', async () => {
// 211 permissions today, so 209 per role.
const expectedPerRole = ALL_PERMISSIONS.length - DEMO_EXCLUDED_PERMISSIONS.length;
// CONTROL: the exclusion list must actually name permissions that exist, or the count
// below would equal the non-demo count and prove nothing.
for (const permission of DEMO_EXCLUDED_PERMISSIONS) {
expect(ALL_PERMISSIONS).toContain(permission);
}
const perRole: Array<{ roleId: string; total: number }> = await dataSource.manager.query(
`SELECT "roleId", COUNT(*) AS total FROM "role_permission" GROUP BY "roleId"`
);
expect(perRole).toHaveLength(EXPECTED_ROLE_COUNT);
for (const { total } of perRole) {
expect(Number(total)).toBe(expectedPerRole);
}
});
it('never grants the deletion permissions to any role', async () => {
const placeholders = DEMO_EXCLUDED_PERMISSIONS.map(() => '?').join(', ');
const [{ total }] = await dataSource.manager.query(
`SELECT COUNT(*) AS total FROM "role_permission" WHERE "permission" IN (${placeholders})`,
DEMO_EXCLUDED_PERMISSIONS
);
expect(Number(total)).toBe(0);
});
});
});
+6 -2
View File
@@ -2,7 +2,8 @@
export default {
displayName: 'desktop-ui-lib',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../jest.angular-defaults.ts'],
coverageDirectory: '../../coverage/packages/desktop-ui-lib',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -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', () => {
@@ -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<ScreenCaptureComponent>;
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(() => {
@@ -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', () => {
@@ -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', () => {
@@ -1,12 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DialogCreateSourceComponent } from './dialog-create-source.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { PluginSourceComponent } from '../../plugin-marketplace-upload/plugin-source/plugin-source.component';
import { CdnFormComponent } from '../../plugin-marketplace-upload/plugin-source/forms/cdn-form/cdn-form.component';
import { NpmFormComponent } from '../../plugin-marketplace-upload/plugin-source/forms/npm-form/npm-form.component';
describe('DialogCreateSourceComponent', () => {
let component: DialogCreateSourceComponent;
let fixture: ComponentFixture<DialogCreateSourceComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DialogCreateSourceComponent]
}).compileComponents();
})
// The plugin source forms use `<nb-hint>` as a plain element styled by their .scss; Nebular has no
// such component, so the app renders it as an unknown element. Tell the test env it is intended.
.overrideComponent(PluginSourceComponent, { add: { schemas: [CUSTOM_ELEMENTS_SCHEMA] } })
.overrideComponent(CdnFormComponent, { add: { schemas: [CUSTOM_ELEMENTS_SCHEMA] } })
.overrideComponent(NpmFormComponent, { add: { schemas: [CUSTOM_ELEMENTS_SCHEMA] } })
.compileComponents();
fixture = TestBed.createComponent(DialogCreateSourceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -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<PluginBasicInformationComponent>;
@@ -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', () => {
@@ -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<PluginMetadataComponent>;
@@ -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', () => {
@@ -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<PluginVersionComponent>;
@@ -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', () => {
@@ -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<SourceContainerComponent>;
@@ -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', () => {
@@ -1,12 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsComponent } from './settings.component';
import { environment } from '@gauzy/ui-config';
import { GAUZY_ENV } from '../constants';
import { FormsModule } from '@angular/forms';
import { LanguageSelectorComponent } from '../language/language-selector.component';
describe('SettingsComponent', () => {
let component: SettingsComponent;
let fixture: ComponentFixture<SettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SettingsComponent]
}).compileComponents();
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 }]
})
// LanguageSelectorComponent (rendered inside settings) binds `[(ngModel)]` on its nb-select but does
// not import FormsModule, so the zone test env rejects the template. The binding is dead in the app
// too (`[(selected)]` next to it does the work); giving the child FormsModule here keeps this spec
// about SettingsComponent. Tracked as a separate template fix.
.overrideComponent(LanguageSelectorComponent, { add: { imports: [FormsModule] } })
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SettingsComponent);
@@ -1,12 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SetupComponent } from './setup.component';
import { environment } from '@gauzy/ui-config';
import { GAUZY_ENV } from '../constants';
import { FormsModule } from '@angular/forms';
import { LanguageSelectorComponent } from '../language/language-selector.component';
describe('SetupComponent', () => {
let component: SetupComponent;
let fixture: ComponentFixture<SetupComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SetupComponent]
}).compileComponents();
imports: [SetupComponent],
// 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 }]
})
// LanguageSelectorComponent (rendered inside setup) binds `[(ngModel)]` on its nb-select but does
// not import FormsModule, so the zone test env rejects the template. The binding is dead in the app
// too (`[(selected)]` next to it does the work); giving the child FormsModule here keeps this spec
// about SetupComponent. Tracked as a separate template fix.
.overrideComponent(LanguageSelectorComponent, { add: { imports: [FormsModule] } })
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(SetupComponent);
@@ -1,5 +1,7 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TaskTableComponent } from './task-table.component';
import { IUser } from '@gauzy/contracts';
import { Store } from '../../../services';
describe('TaskTableComponent', () => {
let component: TaskTableComponent;
let fixture: ComponentFixture<TaskTableComponent>;
@@ -7,6 +9,12 @@ describe('TaskTableComponent', () => {
await TestBed.configureTestingModule({
imports: [TaskTableComponent]
}).compileComponents();
// The task table only builds its data source for a signed-in employee in an organization, which the
// time tracker always has; a bare store has none, and `loading$` was read from a missing source.
const store = TestBed.inject(Store);
store.tenantId = 'tenant-1';
store.organizationId = 'organization-1';
store.user = { id: 'user-1', employee: { id: 'employee-1' } } as IUser;
fixture = TestBed.createComponent(TaskTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -1,11 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TimeTrackerComponent } from './time-tracker.component';
import { AuthService, AuthStrategy } from '../auth/services';
import { environment } from '@gauzy/ui-config';
import { GAUZY_ENV } from '../constants';
describe('TimeTrackerComponent', () => {
let component: TimeTrackerComponent;
let fixture: ComponentFixture<TimeTrackerComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TimeTrackerComponent]
imports: [TimeTrackerComponent],
// The desktop apps provide AuthStrategy, AuthService and this library's GAUZY_ENV (the web
// environment plus their own flags) at bootstrap (apps/desktop/src/main.ts).
providers: [AuthStrategy, AuthService, { provide: GAUZY_ENV, useValue: environment }]
}).compileComponents();
});
beforeEach(() => {
+39
View File
@@ -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> => undefined,
on: noop,
once: noop,
removeListener: noop,
removeAllListeners: noop
},
getGlobal: (): Record<string, unknown> => ({}),
shell: { openExternal: async (): Promise<void> => undefined },
remote: {
app: {
getLocale: (): string => 'en',
getName: (): string => 'gauzy-desktop-test',
getVersion: (): string => '0.0.0',
quit: noop
}
},
log: { ...testConsole, functions: testConsole }
};
+4 -1
View File
@@ -12,7 +12,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -147,7 +147,13 @@ describe('openAiProviderDefinition.transcribe', () => {
it('treats silence as an empty transcript rather than an error', async () => {
// A recording with nothing in it is a valid answer: the user pressed the mic and said nothing.
// Throwing here would surface as a failed dictation and send them looking at their API key.
capture({});
//
// Silence is what OpenAI actually sends for it: a `text` field holding an empty string. The
// fixture used to be `{}` — a body with NO `text` at all — which the shared helper
// deliberately rejects as a malformed response ("never as an empty transcript", pinned in
// ai-chat's openai-compatible-transcribe.spec.ts), so this case contradicted that contract
// and failed on every run.
capture({ text: '' });
await expect(transcribe()).resolves.toBe('');
});
@@ -12,7 +12,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -1
View File
@@ -16,7 +16,10 @@ export default {
// original `.mjs`-only exception left them untransformed: every suite that reaches `Actions`
// (`DocsRowActionsService`) or the ui-core `Store` failed to LOAD rather than fail an
// assertion — which reads as "no tests here" instead of "coverage is zero".
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|(?:.*/)?(@ngneat|@datorama)/)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -16,6 +16,72 @@ const LF = ch(0x0a);
const CR = ch(0x0d);
const NUL = ch(0x00);
/**
* Shortest batch of calls the linearity tests time. One call on the smaller input can take well under a
* millisecond, where timer noise is a large share of the reading, so calls are timed in batches and the
* batch doubled until it runs at least this long. Every reading is then far above the timer's noise.
*/
const MIN_BATCH_MS = 50;
/**
* Absolute ceiling for the FIRST (cold) rejection of the smaller hostile input. The growth ratio below is
* the precise check; this one stops a validator that is linear but uniformly, grossly slow from passing
* it. The old tests asserted 100 ms and 200 ms on exactly that single cold call, and the slowest CI reading
* was 346 ms (two Jest projects side by side on a shared runner), so the ceiling sits about 9x above any
* machine seen so far. Catastrophic backtracking at these sizes runs for seconds to minutes.
*/
const MAX_MS_PER_CALL = 3_000;
/**
* Time per call of `isAllowedUrl(value)`, which must reject the value every time: the median of three
* batches, each at least `MIN_BATCH_MS` long. Growing the batch also warms the JIT and the regexes before
* the three measured batches run.
*/
function msPerCall(value: string): number {
const timeBatch = (calls: number): number => {
let allowed = false;
const started = performance.now();
for (let i = 0; i < calls; i++) {
allowed = isAllowedUrl(value) || allowed;
}
const elapsed = performance.now() - started;
expect(allowed).toBe(false);
return elapsed;
};
let calls = 1;
while (timeBatch(calls) < MIN_BATCH_MS) {
calls *= 2;
}
const batches = [timeBatch(calls), timeBatch(calls), timeBatch(calls)].sort((a, b) => a - b);
return batches[1] / calls;
}
/**
* How much slower `isAllowedUrl` gets when its input grows fourfold: ~4 for linear work, 16 or more
* for the catastrophic backtracking the linearity tests guard against.
*
* Those tests used to assert only an absolute wall-clock bound (`< 100 ms`, `< 200 ms`). That measured
* the machine rather than the algorithm: in CI, with two Jest projects running side by side on the shared
* runner pool, the linear entity case took 346 ms and failed on every run. A ratio of the same code on the
* same machine moments apart cancels the machine out; `MAX_MS_PER_CALL` keeps a generous absolute bound.
* Both sizes are timed the same way (`msPerCall`), and the ratio is taken as measured, with no floor.
*
* The ceiling is checked on ONE call, before anything else runs. A synchronous call cannot be interrupted
* (Jest's timeout included), so a grossly slow validator is reported after that one call instead of after
* the batches and the fourfold input have multiplied the wait. A call that never returns still ends in a
* failure, never a pass: the job's own timeout stops it.
*/
function growthFactor(hostileOfLength: (n: number) => string, n: number): number {
const smallValue = hostileOfLength(n);
const started = performance.now();
expect(isAllowedUrl(smallValue)).toBe(false);
expect(performance.now() - started).toBeLessThan(MAX_MS_PER_CALL);
const small = msPerCall(smallValue);
const large = msPerCall(hostileOfLength(4 * n));
return large / small;
}
describe('isAllowedUrl', () => {
describe('allows what real content is made of', () => {
it.each([
@@ -94,26 +160,18 @@ describe('isAllowedUrl', () => {
});
it('is linear on a long value (no catastrophic backtracking)', () => {
const hostile = `data:text/html;base64,${'A'.repeat(400_000)}`;
const hostile = (n: number) => `data:text/html;base64,${'A'.repeat(n)}`;
const started = Date.now();
const allowed = isAllowedUrl(hostile);
const elapsed = Date.now() - started;
expect(allowed).toBe(false);
expect(elapsed).toBeLessThan(100);
});
// Linear is ~4; quadratic would be ~16.
expect(growthFactor(hostile, 400_000)).toBeLessThan(10);
}, 60_000);
it('is linear on a long run of entities', () => {
const hostile = `${'&#106;'.repeat(60_000)}avascript:alert(1)`;
const hostile = (n: number) => `${'&#106;'.repeat(n)}avascript:alert(1)`;
const started = Date.now();
const allowed = isAllowedUrl(hostile);
const elapsed = Date.now() - started;
expect(allowed).toBe(false);
expect(elapsed).toBeLessThan(200);
});
// Linear is ~4; quadratic would be ~16.
expect(growthFactor(hostile, 60_000)).toBeLessThan(10);
}, 60_000);
});
describe('stripUnsafeUrls', () => {
@@ -0,0 +1,16 @@
// The `test` target in project.json has always pointed at this file, but it did not exist, so
// `nx run plugin-integration-activepieces:test` failed before Jest started ("Can't find a root
// directory while resolving a config file path") and turned the whole Unit Tests run red. The
// plugin has no specs yet; with this config the target runs and passes with no tests (the
// `@nx/jest:jest` default is `passWithNoTests: true`), and a spec added later is picked up.
// Same shape as the other Node-side integration plugins (e.g. integration-make-com).
module.exports = {
displayName: 'plugin-integration-activepieces',
preset: '../../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }]
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../../coverage/packages/plugins/integration-activepieces'
};
@@ -15,6 +15,9 @@
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
}
@@ -1,7 +1,8 @@
export default {
displayName: 'plugin-integration-ai-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/integration-ai-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -12,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,15 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IntegrationAIAuthorizationComponent } from './authorization.component';
import { IntegrationAiUiModule } from '../../integration-ai-ui.module';
import { PipesModule } from '@gauzy/ui-core/shared';
describe('IntegrationAIAuthorizationComponent', () => {
let component: IntegrationAIAuthorizationComponent;
let fixture: ComponentFixture<IntegrationAIAuthorizationComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [IntegrationAIAuthorizationComponent],
// Not standalone: import the plugin NgModule that declares it. PipesModule provides the pipes it injects
// (the app gets them from its root imports).
imports: [IntegrationAiUiModule, PipesModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,11 +1,15 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IntegrationAIViewComponent } from './view.component';
import { IntegrationAiUiModule } from '../../integration-ai-ui.module';
import { PipesModule } from '@gauzy/ui-core/shared';
describe('IntegrationAIViewComponent', () => {
let component: IntegrationAIViewComponent;
let fixture: ComponentFixture<IntegrationAIViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [IntegrationAIViewComponent],
// Not standalone: import the plugin NgModule that declares it. PipesModule provides the pipes it injects
// (the app gets them from its root imports).
imports: [IntegrationAiUiModule, PipesModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -12,7 +12,10 @@ module.exports = {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,7 +1,8 @@
export default {
displayName: 'plugin-integration-github-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/integration-github-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -12,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GithubSettingsComponent } from './settings.component';
import { IntegrationGithubUiModule } from '../../integration-github-ui.module';
describe('GithubSettingsComponent', () => {
let component: GithubSettingsComponent;
let fixture: ComponentFixture<GithubSettingsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GithubSettingsComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [IntegrationGithubUiModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,11 +1,35 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GithubViewComponent } from './view.component';
import { IntegrationGithubUiModule } from '../../integration-github-ui.module';
import { PipesModule } from '@gauzy/ui-core/shared';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
describe('GithubViewComponent', () => {
let component: GithubViewComponent;
let fixture: ComponentFixture<GithubViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GithubViewComponent],
// Not standalone: import the plugin NgModule that declares it. PipesModule provides the pipes it injects
// (the app gets them from its root imports).
imports: [IntegrationGithubUiModule, PipesModule],
providers: [
{
provide: ActivatedRoute,
// A child of the GitHub layout route (integration-github.routes.ts): the parent resolves the
// integration tenant, and the child path carries `:integrationId`.
useValue: {
parent: { data: of({ integration: { id: 'integration-1', name: 'Github' } }) },
snapshot: {
data: { selectors: false },
params: { integrationId: 'integration-1' },
queryParams: {}
},
data: of({ selectors: false }),
params: of({ integrationId: 'integration-1' }),
queryParams: of({})
}
}
],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { GithubWizardComponent } from './wizard.component';
import { IntegrationGithubUiModule } from '../../integration-github-ui.module';
describe('GithubWizardComponent', () => {
let component: GithubWizardComponent;
let fixture: ComponentFixture<GithubWizardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [GithubWizardComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [IntegrationGithubUiModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-integration-hubstaff-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/integration-hubstaff-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HubstaffComponent } from './hubstaff.component';
import { IntegrationHubstaffModule } from '../../integration-hubstaff-ui.module';
describe('HubstaffComponent', () => {
let component: HubstaffComponent;
let fixture: ComponentFixture<HubstaffComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [HubstaffComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [IntegrationHubstaffModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SettingsDialogComponent } from './settings-dialog.component';
import { IntegrationHubstaffModule } from '../../integration-hubstaff-ui.module';
describe('SettingsDialogComponent', () => {
let component: SettingsDialogComponent;
let fixture: ComponentFixture<SettingsDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [SettingsDialogComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [IntegrationHubstaffModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -12,7 +12,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -12,7 +12,10 @@ module.exports = {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -12,7 +12,10 @@ module.exports = {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -29,7 +29,7 @@
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "packages/plugins/integration-sim-ui/jest.config.ts",
"jestConfig": "packages/plugins/integration-sim-ui/jest.config.cts",
"tsConfig": "packages/plugins/integration-sim-ui/tsconfig.spec.json"
}
},
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-integration-upwork-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/integration-upwork-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,9 +1,9 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { NbDialogRef } from '@nebular/theme';
import { TranslateService } from '@ngx-translate/core';
import { UpworkStoreService, ToastrService, ErrorHandlingService } from '@gauzy/ui-core/core';
import { SyncDataSelectionComponent } from './sync-data-selection.component';
import { IntegrationUpworkUiModule } from '../../integration-upwork-ui.module';
describe('SyncDataSelectionComponent', () => {
let component: SyncDataSelectionComponent;
@@ -12,29 +12,32 @@ describe('SyncDataSelectionComponent', () => {
// mocks for injected services
const upworkStoreMock = {
contractsSettings$: of([]),
syncDataWithContractRelated: jasmine.createSpy('syncDataWithContractRelated').and.returnValue(of(null)),
setSelectedEmployeeId: jasmine.createSpy('setSelectedEmployeeId')
syncDataWithContractRelated: jest.fn().mockReturnValue(of(null)),
setSelectedEmployeeId: jest.fn()
};
const toastrMock = { success: jasmine.createSpy('success'), error: jasmine.createSpy('error') };
const toastrMock = { success: jest.fn(), error: jest.fn() };
const dialogRefMock = {};
const errorHandlingMock = { handleError: jasmine.createSpy('handleError') };
const translateServiceMock = { get: () => of('') };
const errorHandlingMock = { handleError: jest.fn() };
beforeEach(async () => {
// reset spy history so each spec starts clean
toastrMock.success.calls.reset();
toastrMock.error.calls.reset();
errorHandlingMock.handleError.calls.reset();
toastrMock.success.mockClear();
toastrMock.error.mockClear();
errorHandlingMock.handleError.mockClear();
await TestBed.configureTestingModule({
declarations: [SyncDataSelectionComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope; the
// mocks below still replace the services it injects.
imports: [IntegrationUpworkUiModule],
// TranslateService is NOT mocked: the root jest.angular-defaults.ts provides a real, loader-less one
// (it returns keys). The partial mock that sat here lacked `instant` and the change streams the
// translate pipe subscribes to, so the template could not render.
providers: [
{ provide: UpworkStoreService, useValue: upworkStoreMock },
{ provide: ToastrService, useValue: toastrMock },
{ provide: NbDialogRef, useValue: dialogRefMock },
{ provide: ErrorHandlingService, useValue: errorHandlingMock },
{ provide: TranslateService, useValue: translateServiceMock }
{ provide: ErrorHandlingService, useValue: errorHandlingMock }
],
teardown: { destroyAfterEach: false }
}).compileComponents();
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UpworkAuthorizeComponent } from './upwork-authorize.component';
import { IntegrationUpworkUiModule } from '../../integration-upwork-ui.module';
describe('UpworkAuthorizeComponent', () => {
let component: UpworkAuthorizeComponent;
let fixture: ComponentFixture<UpworkAuthorizeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UpworkAuthorizeComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [IntegrationUpworkUiModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,37 +1,37 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { UpworkComponent } from './upwork.component';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { Store, UpworkStoreService } from '@gauzy/ui-core/core';
import { TranslateService } from '@ngx-translate/core';
import { IntegrationUpworkUiModule } from '../../integration-upwork-ui.module';
describe('UpworkComponent', () => {
let component: UpworkComponent;
let fixture: ComponentFixture<UpworkComponent>;
// minimal mocks for injected services
const routerMock = { navigate: jasmine.createSpy('navigate') };
const activatedRouteMock = { params: of({}) };
const upworkStoreMock = {
getConfig: jasmine.createSpy('getConfig').and.returnValue(of({}))
getConfig: jest.fn().mockReturnValue(of({}))
};
const storeMock = {
selectedOrganization$: of(null)
};
const translateServiceMock = {
get: () => of(''),
onLangChange: of({})
};
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UpworkComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope; the
// mocks below still replace the services it injects.
imports: [IntegrationUpworkUiModule],
// Router is NOT mocked either: the template's routerLink / routerLinkActive need a real router (its
// events and URL trees), which the root defaults provide; nothing here asserts a navigation.
// TranslateService is NOT mocked: the root jest.angular-defaults.ts provides a real, loader-less one
// (it returns keys). The partial mock that sat here lacked `instant` and the change streams the
// translate pipe subscribes to, so the template could not render.
providers: [
{ provide: Router, useValue: routerMock },
{ provide: ActivatedRoute, useValue: activatedRouteMock },
{ provide: UpworkStoreService, useValue: upworkStoreMock },
{ provide: Store, useValue: storeMock },
{ provide: TranslateService, useValue: translateServiceMock }
{ provide: Store, useValue: storeMock }
],
teardown: { destroyAfterEach: false }
}).compileComponents();
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-job-employee-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/job-employee-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,34 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { JobEmployeeComponent } from './job-employee.component';
import { JobEmployeeModule } from '../../job-employee.module';
import { EmployeesService } from '@gauzy/ui-core/core';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
describe('JobEmployeeComponent', () => {
let component: JobEmployeeComponent;
let fixture: ComponentFixture<JobEmployeeComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [JobEmployeeComponent],
// Not standalone: import the plugin NgModule that declares it. EmployeesService is provided by the host
// feature modules in the app.
imports: [JobEmployeeModule],
providers: [
EmployeesService,
{
provide: ActivatedRoute,
// What job-employee.routes.ts gives the page: its tabset and data-table ids.
useValue: {
snapshot: {
data: { tabsetId: 'job-employee', dataTableId: 'job-employee-page' },
params: {},
queryParams: {}
},
data: of({ tabsetId: 'job-employee', dataTableId: 'job-employee-page' }),
params: of({}),
queryParams: of({})
}
}
],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-job-matching-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/job-matching-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { JobMatchingComponent } from './job-matching.component';
import { JobMatchingModule } from '../../job-matching.module';
describe('JobMatchingComponent', () => {
let component: JobMatchingComponent;
let fixture: ComponentFixture<JobMatchingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [JobMatchingComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [JobMatchingModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-job-proposal-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/job-proposal-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,16 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ProposalTemplateFormComponent } from './proposal-template-form.component';
import { JobProposalTemplateModule } from '../../job-proposal-template.module';
import { EmployeesService } from '@gauzy/ui-core/core';
describe('ProposalTemplateFormComponent', () => {
let component: ProposalTemplateFormComponent;
let fixture: ComponentFixture<ProposalTemplateFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ProposalTemplateFormComponent],
// Not standalone: import its NgModule for the template's real scope.
imports: [JobProposalTemplateModule],
// Provided by the host feature modules in the app.
providers: [EmployeesService],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
@@ -1,11 +1,15 @@
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<ProposalTemplateListComponent>;
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();
});
@@ -2,7 +2,8 @@
export default {
displayName: 'plugin-job-search-ui',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../../jest.angular-defaults.ts'],
coverageDirectory: '../../../coverage/packages/plugins/job-search-ui',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -1,11 +1,13 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { JobSearchComponent } from './job-search.component';
import { JobSearchModule } from '../../job-search.module';
describe('JobSearchComponent', () => {
let component: JobSearchComponent;
let fixture: ComponentFixture<JobSearchComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [JobSearchComponent],
// Not standalone: import the plugin NgModule that declares it, for the template's real scope.
imports: [JobSearchModule],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
+4 -1
View File
@@ -12,7 +12,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -7,10 +7,13 @@ import { Component } from '@angular/core';
*/
@Component({
selector: 'ga-job-layout',
template: ` <router-outlet></router-outlet> `,
// The Jobs section's content region is the page's main landmark (the app shell defines none). Its spec
// has asserted `main[role="main"]` since the layout was added; the template never rendered one.
template: ` <main role="main"><router-outlet></router-outlet></main> `,
styles: [
`
:host {
:host,
main {
display: block;
height: 100%;
}
+4 -1
View File
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -1
View File
@@ -12,7 +12,10 @@ module.exports = {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -13,7 +13,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+4 -3
View File
@@ -15,9 +15,10 @@ export default {
// `@ngneat/effects` and `@datorama/akita` ship ESM from plain `.js` entries; the `.mjs`-only
// exception left them untransformed, so any suite reaching the video store/effects failed to
// LOAD with "Unexpected token". Same fix as @gauzy/plugin-docs-ui.
transformIgnorePatterns: [
'node_modules/(?!.*\\.mjs$|(?:.*/)?(@ngneat|@datorama|uuid|lodash-es|camelcase|nanoid)/)'
],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
+6 -2
View File
@@ -2,7 +2,8 @@
export default {
displayName: 'ui-auth',
preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
// test-setup starts the zone test env; the root defaults add the app-wide TestBed providers (see file).
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts', '<rootDir>/../../jest.angular-defaults.ts'],
coverageDirectory: '../../coverage/packages/ui-auth',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
@@ -13,7 +14,10 @@ export default {
}
]
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
// `transformIgnorePatterns` is inherited from the root `jest.preset.js` (the ESM-only packages plus
// every `.mjs` bundle). Do not redefine it here: a project key REPLACES the preset list, and the one
// that used to sit here carried only the `.mjs` exception, so @datorama/akita, @ngneat/* and
// lodash-es were never transformed and the suites that reach them failed to load.
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
@@ -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<NgxLoginMagicComponent>;
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(() => {
@@ -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<NgxMagicSignInWorkspaceComponent>;
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(() => {

Some files were not shown because too many files have changed in this diff Show More