Browse Source

Update node. (#1286)

* Update node.

* Dummy update

* Fix some tests.

* Cleanup

* Update test.

* Source folder.
pull/1288/head
Sebastian Stehle 8 months ago
committed by GitHub
parent
commit
eec552df41
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      Dockerfile
  2. 2
      README.md
  3. 2
      dev/frontend-build/Dockerfile
  4. 7
      frontend/angular.json
  5. 2
      frontend/eslint.config.js
  6. 606
      frontend/package-lock.json
  7. 4
      frontend/package.json
  8. 18
      frontend/src/app/framework/angular/forms/focus-on-init.directive.spec.ts
  9. 8
      frontend/src/app/framework/angular/forms/focus-on-init.directive.ts
  10. 28
      frontend/src/app/framework/services/loading.service.spec.ts
  11. 8
      frontend/src/app/framework/services/loading.service.ts
  12. 18
      frontend/src/test-setup.ts
  13. 7
      frontend/vitest.config.js

4
Dockerfile

@ -13,7 +13,7 @@ ARG SQUIDEX__BUILD__ARGS
RUN echo "ARGS IS $SQUIDEX__BUILD__ARGS" RUN echo "ARGS IS $SQUIDEX__BUILD__ARGS"
WORKDIR /build WORKDIR /source
# Copy nuget project files. # Copy nuget project files.
COPY backend/Directory.Build.props ./ COPY backend/Directory.Build.props ./
@ -57,7 +57,7 @@ RUN dotnet tool install --tool-path /tools dotnet-dump \
# #
FROM squidex/frontend-build:22.19 AS frontend FROM squidex/frontend-build:22.19 AS frontend
WORKDIR /build WORKDIR /source
ENV CONTINUOUS_INTEGRATION=1 ENV CONTINUOUS_INTEGRATION=1

2
README.md

@ -1,6 +1,6 @@
![Squidex Logo](https://raw.githubusercontent.com/Squidex/squidex/master/media/logo-wide.png "Squidex") ![Squidex Logo](https://raw.githubusercontent.com/Squidex/squidex/master/media/logo-wide.png "Squidex")
# What is Squidex? # What is Squidex??
Squidex is an open source headless CMS and content management hub. In contrast to a traditional CMS Squidex provides a rich API with OData filter and Swagger definitions. It is up to you to build your UI on top of it. It can be website, a native app or just another server. We build it with ASP.NET Core and CQRS and is tested for Windows and Linux on modern browsers. Squidex is an open source headless CMS and content management hub. In contrast to a traditional CMS Squidex provides a rich API with OData filter and Swagger definitions. It is up to you to build your UI on top of it. It can be website, a native app or just another server. We build it with ASP.NET Core and CQRS and is tested for Windows and Linux on modern browsers.

2
dev/frontend-build/Dockerfile

@ -1,4 +1,4 @@
FROM node:22.19.0-slim FROM node:24.13.0-slim
ENV CHROME_BIN=/usr/bin/chromium ENV CHROME_BIN=/usr/bin/chromium

7
frontend/angular.json

@ -170,7 +170,12 @@
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"test": { "test": {
"builder": "@angular/build:unit-test" "builder": "@angular/build:unit-test",
"options": {
"tsConfig": "./tsconfig.spec.json",
"runnerConfig": "vitest.config.js",
"setupFiles": ["./src/test-setup.ts"]
}
}, },
"storybook": { "storybook": {
"builder": "@storybook/angular:start-storybook", "builder": "@storybook/angular:start-storybook",

2
frontend/eslint.config.js

@ -4,7 +4,6 @@ const {
const globals = require("globals"); const globals = require("globals");
const tsParser = require("@typescript-eslint/parser"); const tsParser = require("@typescript-eslint/parser");
const deprecation = require("eslint-plugin-deprecation");
const importPlugin = require("eslint-plugin-import"); const importPlugin = require("eslint-plugin-import");
const typescriptEslint = require("@typescript-eslint/eslint-plugin"); const typescriptEslint = require("@typescript-eslint/eslint-plugin");
@ -42,7 +41,6 @@ module.exports = defineConfig([{
), ),
plugins: { plugins: {
deprecation,
import: importPlugin, import: importPlugin,
"@typescript-eslint": typescriptEslint, "@typescript-eslint": typescriptEslint,
}, },

606
frontend/package-lock.json

File diff suppressed because it is too large

4
frontend/package.json

@ -98,8 +98,6 @@
"@typescript-eslint/parser": "^8.54.0", "@typescript-eslint/parser": "^8.54.0",
"copy-webpack-plugin": "^13.0.1", "copy-webpack-plugin": "^13.0.1",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-config-airbnb-typescript": "18.0.0",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-import": "2.32.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-storybook": "^10.2.7", "eslint-plugin-storybook": "^10.2.7",
@ -113,7 +111,7 @@
"stylelint-scss": "7.0.0", "stylelint-scss": "7.0.0",
"typescript": "5.9.3", "typescript": "5.9.3",
"vite-tsconfig-paths": "^6.0.5", "vite-tsconfig-paths": "^6.0.5",
"vitest": "^4.0.18" "vitest": "^4.0.8"
}, },
"overrides": { "overrides": {
"ng2-charts": { "ng2-charts": {

18
frontend/src/app/framework/angular/forms/focus-on-init.directive.spec.ts

@ -30,27 +30,21 @@ describe('FocusOnInitDirective', () => {
it('should call focus on element if init', async () => { it('should call focus on element if init', async () => {
const directive = new FocusOnInitDirective(element); const directive = new FocusOnInitDirective(element);
directive.scheduler = action => action();
directive.select = false; directive.select = false;
directive.ngAfterViewInit(); directive.ngAfterViewInit();
setTimeout(() => { expect(isFocusCalled).toBeTruthy();
expect(isFocusCalled).toBeTruthy(); expect(isSelectCalled).toBeFalsy();
expect(isSelectCalled).toBeFalsy();
}, 200);
}); });
it('should call select on element if init', async () => { it('should call select on element if init', async () => {
const directive = new FocusOnInitDirective(element); const directive = new FocusOnInitDirective(element);
directive.scheduler = action => action();
directive.select = true; directive.select = true;
directive.ngAfterViewInit(); directive.ngAfterViewInit();
setTimeout(() => { expect(isFocusCalled).toBeTruthy();
expect(isFocusCalled).toBeTruthy(); expect(isSelectCalled).toBeTruthy();
expect(isSelectCalled).toBeTruthy();
}, 200);
}); });
}); });

8
frontend/src/app/framework/angular/forms/focus-on-init.directive.ts

@ -18,6 +18,10 @@ export class FocusOnInitDirective implements AfterViewInit {
@Input({ alias: 'sqxFocusOnInit', transform: booleanAttribute }) @Input({ alias: 'sqxFocusOnInit', transform: booleanAttribute })
public enabled = true; public enabled = true;
public scheduler: ((action: (() => void)) => void) = action => {
setTimeout(action, 200);
};
constructor( constructor(
private readonly element: ElementRef<HTMLElement>, private readonly element: ElementRef<HTMLElement>,
) { ) {
@ -28,7 +32,7 @@ export class FocusOnInitDirective implements AfterViewInit {
return; return;
} }
setTimeout(() => { this.scheduler(() => {
if (Types.isFunction(this.element.nativeElement.focus)) { if (Types.isFunction(this.element.nativeElement.focus)) {
this.element.nativeElement.focus(); this.element.nativeElement.focus();
} }
@ -40,6 +44,6 @@ export class FocusOnInitDirective implements AfterViewInit {
input.select(); input.select();
} }
} }
}, 100); });
} }
} }

28
frontend/src/app/framework/services/loading.service.spec.ts

@ -22,9 +22,9 @@ describe('LoadingService', () => {
it('should set to loaded', () => { it('should set to loaded', () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
loadingService.scheduler = action => action();
let state = false; let state = false;
loadingService.loading.subscribe(v => { loadingService.loading.subscribe(v => {
state = v; state = v;
}); });
@ -35,9 +35,9 @@ describe('LoadingService', () => {
it('should set to loaded on navigation start', () => { it('should set to loaded on navigation start', () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
loadingService.scheduler = action => action();
let state = false; let state = false;
loadingService.loading.subscribe(v => { loadingService.loading.subscribe(v => {
state = v; state = v;
}); });
@ -51,7 +51,6 @@ describe('LoadingService', () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
let state = false; let state = false;
loadingService.loading.subscribe(v => { loadingService.loading.subscribe(v => {
state = v; state = v;
}); });
@ -63,42 +62,35 @@ describe('LoadingService', () => {
it('should not unset from loaded delayed', async () => { it('should not unset from loaded delayed', async () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
loadingService.scheduler = action => action();
let state = false; let state = false;
loadingService.loading.subscribe(v => { loadingService.loading.subscribe(v => {
state = v; state = v;
}); });
loadingService.startLoading('1'); loadingService.startLoading('1');
loadingService.completeLoading('1'); loadingService.completeLoading('1');
setTimeout(() => { expect(state).toBeFalsy();
expect(state).toBeFalsy();
}, 400);
}); });
it('should not unset from loaded delayed on navigation event', async () => { it('should not unset from loaded delayed on navigation event', async () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
loadingService.scheduler = action => action();
let state = false; let state = false;
loadingService.loading.subscribe(v => { loadingService.loading.subscribe(v => {
state = v; state = v;
}); });
events.next(new NavigationStart(0, '')); events.next(new NavigationStart(0, ''));
events.next(new NavigationError(0, '', 0)); events.next(new NavigationError(0, '', 0));
setTimeout(() => { expect(state).toBeFalsy();
expect(state).toBeFalsy();
}, 400);
}); });
it('should set back to loaded after several completions', async () => { it('should set back to loaded after several completions', async () => {
const loadingService = new LoadingService(<any>{ events }); const loadingService = new LoadingService(<any>{ events });
loadingService.scheduler = action => action();
let state = false; let state = false;
@ -110,10 +102,6 @@ describe('LoadingService', () => {
loadingService.completeLoading('1'); loadingService.completeLoading('1');
loadingService.startLoading('2'); loadingService.startLoading('2');
setTimeout(() => { expect(state).toBeTruthy();
expect(state).toBeTruthy();
}, 400);
}); });
}); });

8
frontend/src/app/framework/services/loading.service.ts

@ -22,6 +22,10 @@ export class LoadingService implements OnDestroy {
return this.loading$.pipe(map(x => x > 0)); return this.loading$.pipe(map(x => x > 0));
} }
public scheduler: ((action: (() => void)) => void) = action => {
setTimeout(action, 200);
};
constructor(router: Router) { constructor(router: Router) {
this.routerSubscription = this.routerSubscription =
router.events.subscribe(event => { router.events.subscribe(event => {
@ -54,13 +58,13 @@ export class LoadingService implements OnDestroy {
if (this.loadingOperations[key]) { if (this.loadingOperations[key]) {
delete this.loadingOperations[key]; delete this.loadingOperations[key];
setTimeout(() => { this.scheduler(() => {
const value = this.loading$.value; const value = this.loading$.value;
if (value > 0) { if (value > 0) {
this.loading$.next(value - 1); this.loading$.next(value - 1);
} }
}, 250); });
} }
} }
} }

18
frontend/src/test-setup.ts

@ -0,0 +1,18 @@
import { getTestBed, TestBed } from "@angular/core/testing";
import { BrowserTestingModule, platformBrowserTesting } from "@angular/platform-browser/testing";
import { afterEach } from "vitest";
afterEach(() => {
TestBed.resetTestingModule();
});
// Only initialize test environment if it hasn't been initialized yet
// This prevents errors when vitest runs tests in parallel (e.g., in CI)
try {
getTestBed().initTestEnvironment(
BrowserTestingModule,
platformBrowserTesting(),
);
} catch {
// Test environment already initialized, ignore the error
}

7
frontend/vitest.config.js

@ -0,0 +1,7 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
passWithNoTests: true,
},
});
Loading…
Cancel
Save