mirror of
https://github.com/ever-co/ever-gauzy.git
synced 2026-10-02 01:54:50 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'
|
||||
]
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
+7
-1
@@ -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();
|
||||
|
||||
+9
-1
@@ -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();
|
||||
|
||||
+9
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+7
-1
@@ -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();
|
||||
});
|
||||
|
||||
+7
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+7
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+7
-1
@@ -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();
|
||||
|
||||
+7
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+7
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+7
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+9
-2
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+28
-1
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -47,7 +47,10 @@
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/mcp-auth/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 }
|
||||
]
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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')]
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
+2
@@ -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', () => {
|
||||
|
||||
+2
@@ -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', () => {
|
||||
|
||||
+11
-1
@@ -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();
|
||||
|
||||
+9
@@ -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', () => {
|
||||
|
||||
+8
@@ -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', () => {
|
||||
|
||||
+8
@@ -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', () => {
|
||||
|
||||
+3
@@ -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);
|
||||
|
||||
+8
@@ -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(() => {
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = `${'j'.repeat(60_000)}avascript:alert(1)`;
|
||||
const hostile = (n: number) => `${'j'.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',
|
||||
|
||||
+5
-1
@@ -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',
|
||||
|
||||
+3
-1
@@ -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();
|
||||
});
|
||||
|
||||
+25
-1
@@ -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();
|
||||
});
|
||||
|
||||
+3
-1
@@ -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',
|
||||
|
||||
+3
-1
@@ -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();
|
||||
});
|
||||
|
||||
+3
-1
@@ -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',
|
||||
|
||||
+15
-12
@@ -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();
|
||||
|
||||
+3
-1
@@ -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();
|
||||
});
|
||||
|
||||
+12
-12
@@ -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',
|
||||
|
||||
+24
-1
@@ -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',
|
||||
|
||||
+3
-1
@@ -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',
|
||||
|
||||
+6
-1
@@ -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();
|
||||
});
|
||||
|
||||
+5
-1
@@ -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',
|
||||
|
||||
+3
-1
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
+5
-1
@@ -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
Reference in New Issue
Block a user