feat: support Angular 20 (#1220)
Adds Angular 20 support. This also remove the custom jest setup as it was not compatible with Angular 20 and uses builtin support for vitest in Angular 20. For this, Angular has been removed from the jest setup and the pipeline runs it separately now. Fixes #1206 --------- Signed-off-by: Lukas Reining <lukas.reining@codecentric.de>
This commit is contained in:
parent
07af3a9eda
commit
aa232a9d6a
|
|
@ -37,8 +37,11 @@ jobs:
|
|||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Test
|
||||
run: npm run test
|
||||
- name: Test Jest Projects
|
||||
run: npm run test:jest
|
||||
|
||||
- name: Test Angular SDK
|
||||
run: npm run test:angular
|
||||
|
||||
codecov-and-docs:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -190,26 +190,6 @@ export default {
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'angular',
|
||||
testEnvironment: 'jsdom',
|
||||
preset: 'jest-preset-angular',
|
||||
testMatch: ['<rootDir>/packages/angular/projects/angular-sdk/src/**/*.spec.{ts,tsx}'],
|
||||
setupFilesAfterEnv: ['<rootDir>/packages/angular/setup-jest.ts'],
|
||||
moduleNameMapper: {
|
||||
'@openfeature/core': '<rootDir>/packages/shared/src',
|
||||
'@openfeature/web-sdk': '<rootDir>/packages/web/src',
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.(ts|js|html|svg)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/packages/angular/tsconfig.json',
|
||||
isolatedModules: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,9 @@
|
|||
"private": true,
|
||||
"description": "OpenFeature SDK for JavaScript",
|
||||
"scripts": {
|
||||
"test": "jest --selectProjects=shared --selectProjects=server --selectProjects=web --selectProjects=react --selectProjects=angular --selectProjects=nest --silent",
|
||||
"test": "npm run test:jest && npm run test:angular",
|
||||
"test:jest": "jest --selectProjects=shared --selectProjects=server --selectProjects=web --selectProjects=react --selectProjects=nest --silent",
|
||||
"test:angular": "npm run test:coverage --workspace=packages/angular",
|
||||
"e2e-server": "git submodule update --init --recursive && shx cp test-harness/features/evaluation.feature packages/server/e2e/features && jest --selectProjects=server-e2e --verbose",
|
||||
"e2e-web": "git submodule update --init --recursive && shx cp test-harness/features/evaluation.feature packages/web/e2e/features && jest --selectProjects=web-e2e --verbose",
|
||||
"e2e": "npm run e2e-server && npm run e2e-web",
|
||||
|
|
@ -54,8 +56,6 @@
|
|||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-preset-angular": "^14.2.4",
|
||||
"ng-packagr": "^18.2.1",
|
||||
"prettier": "^3.2.5",
|
||||
"react": "^18.2.0",
|
||||
"rollup": "^4.0.0",
|
||||
|
|
@ -69,9 +69,6 @@
|
|||
"typescript": "^4.7.4",
|
||||
"uuid": "^11.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/shared",
|
||||
"packages/server",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"prefix": "lib",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"builder": "@angular/build:ng-packagr",
|
||||
"options": {
|
||||
"project": "projects/angular-sdk/ng-package.json"
|
||||
},
|
||||
|
|
@ -32,6 +32,15 @@
|
|||
"projects/angular-sdk/**/*.html"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular/build:unit-test",
|
||||
"options": {
|
||||
"tsConfig": "projects/angular-sdk/tsconfig.spec.json",
|
||||
"providersFile": "projects/angular-sdk/src/test-provider.ts",
|
||||
"runner": "vitest",
|
||||
"buildTarget": "::development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,37 +7,42 @@
|
|||
"lint": "ng lint",
|
||||
"lint:fix": "ng lint --fix",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "jest",
|
||||
"test": "ng test",
|
||||
"test:coverage": "ng test --no-watch --code-coverage",
|
||||
"build": "ng build && npm run postbuild",
|
||||
"postbuild": "shx cp ./../../LICENSE ./dist/angular/LICENSE",
|
||||
"publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm --prefix dist/angular run current-published-version -s)\" = \"$(npm --prefix dist/angular run current-version -s)\" ]; then echo 'already published, skipping'; else cd dist/angular && npm publish --access public; fi"
|
||||
},
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^19.0.0",
|
||||
"@angular-eslint/builder": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin": "19.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "19.3.0",
|
||||
"@angular-eslint/schematics": "19.3.0",
|
||||
"@angular-eslint/template-parser": "19.3.0",
|
||||
"@angular/animations": "^19.0.0",
|
||||
"@angular/cli": "^19.0.0",
|
||||
"@angular/common": "^19.0.0",
|
||||
"@angular/compiler": "^19.0.0",
|
||||
"@angular/compiler-cli": "^19.0.0",
|
||||
"@angular/core": "^19.0.0",
|
||||
"@angular/forms": "^19.0.0",
|
||||
"@angular/platform-browser": "^19.0.0",
|
||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||
"@angular/router": "^19.0.0",
|
||||
"@angular-eslint/builder": "^20.1.1",
|
||||
"@angular-eslint/eslint-plugin": "^20.1.1",
|
||||
"@angular-eslint/eslint-plugin-template": "^20.1.1",
|
||||
"@angular-eslint/schematics": "^20.1.1",
|
||||
"@angular-eslint/template-parser": "^20.1.1",
|
||||
"@angular/animations": "^20.1.1",
|
||||
"@angular/build": "^20.1.1",
|
||||
"@angular/cli": "^20.1.1",
|
||||
"@angular/common": "^20.1.1",
|
||||
"@angular/compiler": "^20.1.1",
|
||||
"@angular/compiler-cli": "^20.1.1",
|
||||
"@angular/core": "^20.1.1",
|
||||
"@angular/forms": "^20.1.1",
|
||||
"@angular/platform-browser": "^20.1.1",
|
||||
"@angular/platform-browser-dynamic": "^20.1.1",
|
||||
"@angular/router": "^20.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"eslint": "^8.57.0",
|
||||
"jest-preset-angular": "^14.2.4",
|
||||
"ng-packagr": "^19.0.0",
|
||||
"jsdom": "^26.1.0",
|
||||
"ng-packagr": "^20.1.0",
|
||||
"playwright": "^1.53.2",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.2.4",
|
||||
"zone.js": "~0.15.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For Angular's browser support policy, please see:
|
||||
# https://angular.dev/reference/versions#browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
Chrome >= 107
|
||||
ChromeAndroid >= 107
|
||||
Edge >= 107
|
||||
Firefox >= 104
|
||||
FirefoxAndroid >= 104
|
||||
Safari >= 16
|
||||
iOS >= 16
|
||||
|
||||
|
|
@ -17,18 +17,18 @@
|
|||
"prepack": "shx cp ./../../../../LICENSE ./LICENSE"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^16.2.12 || ^17.3.0 || ^18.0.0 || ^19.0.0",
|
||||
"@angular/core": "^16.2.12 || ^17.3.0 || ^18.0.0 || ^19.0.0",
|
||||
"@angular/common": "^16.2.12 || ^17.3.0 || ^18.0.0 || ^19.0.0 || ^20.0.0",
|
||||
"@angular/core": "^16.2.12 || ^17.3.0 || ^18.0.0 || ^19.0.0 || ^20.0.0",
|
||||
"@openfeature/web-sdk": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openfeature/core": "*",
|
||||
"@openfeature/web-sdk": "*",
|
||||
"@angular/common": "^19.0.0",
|
||||
"@angular/core": "^19.0.0"
|
||||
"@openfeature/core": "^1.8.1",
|
||||
"@openfeature/web-sdk": "^1.5.0",
|
||||
"@angular/common": "^20.1.2",
|
||||
"@angular/core": "^20.1.2"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"keywords": [
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ describe('FeatureFlagDirective', () => {
|
|||
await expectRenderedText(fixture, 'case-2', 'Flag On');
|
||||
|
||||
await updateFlagValue(provider, false);
|
||||
fixture.detectChanges(); // Ensure change detection after flag update
|
||||
await expectRenderedText(fixture, 'case-2', 'Flag Off');
|
||||
});
|
||||
});
|
||||
|
|
@ -393,7 +394,9 @@ describe('FeatureFlagDirective', () => {
|
|||
await waitForClientReady(client);
|
||||
await expectRenderedText(fixture, 'case-6', 'Flag On');
|
||||
|
||||
fixture.componentInstance.specialFlagKey = 'new-test-flag';
|
||||
fixture.componentRef.setInput('specialFlagKey', 'new-test-flag');
|
||||
await fixture.whenStable();
|
||||
|
||||
await expectRenderedText(fixture, 'case-6', 'Flag Off');
|
||||
});
|
||||
|
||||
|
|
@ -445,7 +448,9 @@ describe('FeatureFlagDirective', () => {
|
|||
);
|
||||
await OpenFeature.setProviderAndWait(newDomain, newProvider);
|
||||
|
||||
fixture.componentInstance.domain = newDomain;
|
||||
fixture.componentRef.setInput('domain', newDomain);
|
||||
await fixture.whenStable();
|
||||
|
||||
await expectRenderedText(fixture, 'case-6', 'Flag Off');
|
||||
});
|
||||
});
|
||||
|
|
@ -560,8 +565,7 @@ async function createTestingModule(config?: {
|
|||
],
|
||||
}).createComponent(TestComponent);
|
||||
|
||||
fixture.componentInstance.domain = domain;
|
||||
fixture.detectChanges();
|
||||
fixture.componentRef.setInput('domain', domain);
|
||||
await fixture.whenStable();
|
||||
|
||||
const client = OpenFeature.getClient(domain);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
OnInit,
|
||||
TemplateRef,
|
||||
ViewContainerRef,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
Client,
|
||||
|
|
@ -35,6 +36,9 @@ class FeatureFlagDirectiveContext<T extends FlagValue> {
|
|||
selector: '[featureFlag]',
|
||||
})
|
||||
export abstract class FeatureFlagDirective<T extends FlagValue> implements OnInit, OnDestroy, OnChanges {
|
||||
protected _changeDetectorRef: ChangeDetectorRef;
|
||||
protected _viewContainerRef: ViewContainerRef;
|
||||
|
||||
protected _featureFlagDefault: T;
|
||||
protected _featureFlagDomain: string | undefined;
|
||||
|
||||
|
|
@ -64,13 +68,7 @@ export abstract class FeatureFlagDirective<T extends FlagValue> implements OnIni
|
|||
protected _reconcilingTemplateRef: TemplateRef<FeatureFlagDirectiveContext<T>> | null;
|
||||
protected _reconcilingViewRef: EmbeddedViewRef<unknown> | null;
|
||||
|
||||
protected constructor(
|
||||
protected _changeDetectorRef: ChangeDetectorRef,
|
||||
protected _viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef<FeatureFlagDirectiveContext<T>>,
|
||||
) {
|
||||
this._thenTemplateRef = templateRef;
|
||||
}
|
||||
protected constructor() {}
|
||||
|
||||
set featureFlagDomain(domain: string | undefined) {
|
||||
/**
|
||||
|
|
@ -232,6 +230,10 @@ export abstract class FeatureFlagDirective<T extends FlagValue> implements OnIni
|
|||
selector: '[booleanFeatureFlag]',
|
||||
})
|
||||
export class BooleanFeatureFlagDirective extends FeatureFlagDirective<boolean> implements OnChanges {
|
||||
override _changeDetectorRef = inject(ChangeDetectorRef);
|
||||
override _viewContainerRef = inject(ViewContainerRef);
|
||||
override _thenTemplateRef = inject<TemplateRef<FeatureFlagDirectiveContext<boolean>>>(TemplateRef);
|
||||
|
||||
/**
|
||||
* The key of the boolean feature flag.
|
||||
*/
|
||||
|
|
@ -242,12 +244,8 @@ export class BooleanFeatureFlagDirective extends FeatureFlagDirective<boolean> i
|
|||
*/
|
||||
@Input({ required: true }) booleanFeatureFlagDefault: boolean;
|
||||
|
||||
constructor(
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef<FeatureFlagDirectiveContext<boolean>>,
|
||||
) {
|
||||
super(_changeDetectorRef, _viewContainerRef, templateRef);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
override ngOnChanges() {
|
||||
|
|
@ -347,6 +345,10 @@ export class BooleanFeatureFlagDirective extends FeatureFlagDirective<boolean> i
|
|||
selector: '[numberFeatureFlag]',
|
||||
})
|
||||
export class NumberFeatureFlagDirective extends FeatureFlagDirective<number> implements OnChanges {
|
||||
override _changeDetectorRef = inject(ChangeDetectorRef);
|
||||
override _viewContainerRef = inject(ViewContainerRef);
|
||||
override _thenTemplateRef = inject<TemplateRef<FeatureFlagDirectiveContext<number>>>(TemplateRef);
|
||||
|
||||
/**
|
||||
* The key of the number feature flag.
|
||||
*/
|
||||
|
|
@ -362,12 +364,8 @@ export class NumberFeatureFlagDirective extends FeatureFlagDirective<number> imp
|
|||
*/
|
||||
@Input({ required: false }) numberFeatureFlagValue?: number;
|
||||
|
||||
constructor(
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef<FeatureFlagDirectiveContext<number>>,
|
||||
) {
|
||||
super(_changeDetectorRef, _viewContainerRef, templateRef);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
override ngOnChanges() {
|
||||
|
|
@ -467,6 +465,10 @@ export class NumberFeatureFlagDirective extends FeatureFlagDirective<number> imp
|
|||
selector: '[stringFeatureFlag]',
|
||||
})
|
||||
export class StringFeatureFlagDirective extends FeatureFlagDirective<string> implements OnChanges {
|
||||
override _changeDetectorRef = inject(ChangeDetectorRef);
|
||||
override _viewContainerRef = inject(ViewContainerRef);
|
||||
override _thenTemplateRef = inject<TemplateRef<FeatureFlagDirectiveContext<string>>>(TemplateRef);
|
||||
|
||||
/**
|
||||
* The key of the string feature flag.
|
||||
*/
|
||||
|
|
@ -482,12 +484,8 @@ export class StringFeatureFlagDirective extends FeatureFlagDirective<string> imp
|
|||
*/
|
||||
@Input({ required: false }) stringFeatureFlagValue?: string;
|
||||
|
||||
constructor(
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef<FeatureFlagDirectiveContext<string>>,
|
||||
) {
|
||||
super(_changeDetectorRef, _viewContainerRef, templateRef);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
override ngOnChanges() {
|
||||
|
|
@ -587,6 +585,10 @@ export class StringFeatureFlagDirective extends FeatureFlagDirective<string> imp
|
|||
selector: '[objectFeatureFlag]',
|
||||
})
|
||||
export class ObjectFeatureFlagDirective<T extends JsonValue> extends FeatureFlagDirective<T> implements OnChanges {
|
||||
override _changeDetectorRef = inject(ChangeDetectorRef);
|
||||
override _viewContainerRef = inject(ViewContainerRef);
|
||||
override _thenTemplateRef = inject<TemplateRef<FeatureFlagDirectiveContext<T>>>(TemplateRef);
|
||||
|
||||
/**
|
||||
* The key of the object feature flag.
|
||||
*/
|
||||
|
|
@ -602,12 +604,8 @@ export class ObjectFeatureFlagDirective<T extends JsonValue> extends FeatureFlag
|
|||
*/
|
||||
@Input({ required: false }) objectFeatureFlagValue?: T;
|
||||
|
||||
constructor(
|
||||
_changeDetectorRef: ChangeDetectorRef,
|
||||
_viewContainerRef: ViewContainerRef,
|
||||
templateRef: TemplateRef<FeatureFlagDirectiveContext<T>>,
|
||||
) {
|
||||
super(_changeDetectorRef, _viewContainerRef, templateRef);
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
override ngOnChanges() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
import { provideZonelessChangeDetection } from '@angular/core';
|
||||
|
||||
export default [provideZonelessChangeDetection()];
|
||||
|
|
@ -3,15 +3,20 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jest",
|
||||
"vitest/globals",
|
||||
"node"
|
||||
],
|
||||
"paths": {
|
||||
"angular": [
|
||||
"./dist/angular"
|
||||
]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts",
|
||||
"setup-jest.ts"
|
||||
"src/test-provider.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
import 'jest-preset-angular/setup-jest';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
|
||||
|
|
@ -23,8 +23,8 @@
|
|||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "ES2015",
|
||||
"module": "ES2015",
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"useDefineForClassFields": false,
|
||||
"strictNullChecks": false,
|
||||
"lib": [
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jest"
|
||||
"vitest/globals",
|
||||
"node"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
"projects/angular-sdk/src/**/*.spec.ts",
|
||||
"projects/angular-sdk/src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -18,3 +18,4 @@ export abstract class OpenFeatureError extends Error {
|
|||
this.cause = options?.cause;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue