Files
privacy.sexy/tests/unit/application/Parser/ApplicationParser.spec.ts
undergroundwires 736590558b Migrate web builds from Vue CLI to Vite
This commit changes the web application's build, transpilation and
minification process from Vue CLI to Vite. This shift paves the way for
a full migration to Vite as the primary build tool (#230).

Configuration changes:

- `.vscode/extensions.json`: Update recommended plugins, replacing
  unmaintained ones with official recommendations.
- Legacy browser support:
  - Use `@vitejs/plugin-legacy` to transpile for older browsers.
  - Remove `core-js` dependency and `babel.config.cjs` configuration as
    they're now handled by the legacy plugin.
  - Delete `@babel/preset-typescript` and `@babel/preset-typescript`
    dependencies as legacy plugin handles babel dependencies by default.
  - Add `terser` dependency that's used by the legacy plugin for
    minification, as per Vite's official documentation.
- `tsconfig.json`:
  - Remove obsolete `webpack-env` types.
  - Add `"resolveJsonModule": true` to be able to read JSON files in
    right way.
  - Use correct casing as configuration values.
  - Simplify `lib` to align with Vite and Vue starter configuration.
  - Add `"skipLibCheck": true` as `npm run build` now runs `tsc` which
    fails on inconsistent typings inside `node_modules` due to npm's
    weak dependency resoultion.
- PostCSS:
  - Add `autoprefixer` as dependency, no longer installed by Vue CLI.
  - Epxlicitly added `postcss` as dependency to anticipate potential
    peer dependency changes.
- Remove related `@vue/cli` dependencies.
- Remove `sass-loader` as Vite has native CSS preprocessing support.
- Run integration tests with `jsdom` environment so `window` object can
  be used.

Client-side changes:

- Abstract build tool specific environment variable population.
  Environment variables were previously populated by Vue CLI and now by
  Vite but not having an abstraction caused issues. This abstraction
  solves build errors and allows easier future migrations and testing.
- Change Vue CLI-specific `~@` aliases to `@` to be able to compile with
  Vite.
- Update types in LiquorTree to satisfy `tsc`.
- Remove Vue CLI-specific workaround from `src/presentation/main.ts`.

Restructuring:

- Move `public/` to `presentation/` to align with the layered structure,
  which was not possible with Vue CLI.
- Move `index.html` to web root instead of having it inside `public/` to
  align with official recommended structure.
- Move logic shared by both integration and unit tests to
  `tests/shared`.
- Move logo creation script to `scripts/` and its npm command to include
  `build` to align with rest of the structure.
2023-08-23 23:12:56 +02:00

204 lines
7.5 KiB
TypeScript

/* eslint-disable max-classes-per-file */
import { describe, it, expect } from 'vitest';
import type { CollectionData } from '@/application/collections/';
import { parseProjectInformation } from '@/application/Parser/ProjectInformationParser';
import { CategoryCollectionParserType, parseApplication } from '@/application/Parser/ApplicationParser';
import { IAppMetadata } from '@/infrastructure/Metadata/IAppMetadata';
import WindowsData from '@/application/collections/windows.yaml';
import MacOsData from '@/application/collections/macos.yaml';
import LinuxData from '@/application/collections/linux.yaml';
import { IProjectInformation } from '@/domain/IProjectInformation';
import { ProjectInformation } from '@/domain/ProjectInformation';
import { ICategoryCollection } from '@/domain/ICategoryCollection';
import { OperatingSystem } from '@/domain/OperatingSystem';
import { getEnumValues } from '@/application/Common/Enum';
import { CategoryCollectionStub } from '@tests/unit/shared/Stubs/CategoryCollectionStub';
import { CollectionDataStub } from '@tests/unit/shared/Stubs/CollectionDataStub';
import { getAbsentCollectionTestCases, AbsentObjectTestCases } from '@tests/unit/shared/TestCases/AbsentTests';
import { AppMetadataStub } from '@tests/unit/shared/Stubs/AppMetadataStub';
describe('ApplicationParser', () => {
describe('parseApplication', () => {
describe('parser', () => {
it('returns result from the parser', () => {
// arrange
const os = OperatingSystem.macOS;
const data = new CollectionDataStub();
const expected = new CategoryCollectionStub()
.withOs(os);
const parser = new CategoryCollectionParserSpy()
.setUpReturnValue(data, expected)
.mockParser();
const sut = new ApplicationParserBuilder()
.withCategoryCollectionParser(parser)
.withCollectionsData([data]);
// act
const app = sut.parseApplication();
// assert
const actual = app.getCollection(os);
expect(expected).to.equal(actual);
});
});
describe('processEnv', () => {
it('used to parse expected project information', () => {
// arrange
const env = new AppMetadataStub();
const expected = parseProjectInformation(env);
const parserSpy = new CategoryCollectionParserSpy();
const parserMock = parserSpy.mockParser();
const sut = new ApplicationParserBuilder()
.withCategoryCollectionParser(parserMock);
// act
const app = sut.parseApplication();
// assert
expect(expected).to.deep.equal(app.info);
expect(parserSpy.arguments.map((arg) => arg.info).every((info) => info === expected));
});
});
describe('collectionsData', () => {
describe('set as expected', () => {
// arrange
const testCases = [
{
name: 'single collection',
input: [new CollectionDataStub()],
output: [new CategoryCollectionStub().withOs(OperatingSystem.macOS)],
},
{
name: 'multiple collections',
input: [
new CollectionDataStub().withOs('windows'),
new CollectionDataStub().withOs('macos'),
],
output: [
new CategoryCollectionStub().withOs(OperatingSystem.macOS),
new CategoryCollectionStub().withOs(OperatingSystem.Windows),
],
},
];
// act
for (const testCase of testCases) {
it(testCase.name, () => {
let parserSpy = new CategoryCollectionParserSpy();
for (let i = 0; i < testCase.input.length; i++) {
parserSpy = parserSpy.setUpReturnValue(testCase.input[i], testCase.output[i]);
}
const sut = new ApplicationParserBuilder()
.withCategoryCollectionParser(parserSpy.mockParser())
.withCollectionsData(testCase.input);
// act
const app = sut.parseApplication();
// assert
expect(app.collections).to.deep.equal(testCase.output);
});
}
});
it('defaults to expected data', () => {
// arrange
const expected = [WindowsData, MacOsData, LinuxData];
const parserSpy = new CategoryCollectionParserSpy();
const sut = new ApplicationParserBuilder()
.withCollectionsData(undefined)
.withCategoryCollectionParser(parserSpy.mockParser());
// act
sut.parseApplication();
// assert
const actual = parserSpy.arguments.map((args) => args.data);
expect(actual).to.deep.equal(expected);
});
describe('throws when data is invalid', () => {
// arrange
const testCases = [
...getAbsentCollectionTestCases<CollectionData>().map((testCase) => ({
name: `given absent collection "${testCase.valueName}"`,
value: testCase.absentValue,
expectedError: 'missing collections',
})).filter((test) => test.value !== undefined /* the default value is set */),
...AbsentObjectTestCases.map((testCase) => ({
name: `given absent item "${testCase.valueName}"`,
value: [testCase.absentValue],
expectedError: 'missing collection provided',
})),
];
for (const testCase of testCases) {
it(testCase.name, () => {
const sut = new ApplicationParserBuilder()
.withCollectionsData(testCase.value);
// act
const act = () => sut.parseApplication();
// assert
expect(act).to.throw(testCase.expectedError);
});
}
});
});
});
});
class ApplicationParserBuilder {
private categoryCollectionParser: CategoryCollectionParserType = new CategoryCollectionParserSpy()
.mockParser();
private environment: IAppMetadata = new AppMetadataStub();
private collectionsData: CollectionData[] = [new CollectionDataStub()];
public withCategoryCollectionParser(
categoryCollectionParser: CategoryCollectionParserType,
): this {
this.categoryCollectionParser = categoryCollectionParser;
return this;
}
public withEnvironment(
environment: IAppMetadata,
): this {
this.environment = environment;
return this;
}
public withCollectionsData(collectionsData: CollectionData[]): this {
this.collectionsData = collectionsData;
return this;
}
public parseApplication(): ReturnType<typeof parseApplication> {
return parseApplication(
this.categoryCollectionParser,
this.environment,
this.collectionsData,
);
}
}
class CategoryCollectionParserSpy {
public arguments = new Array<{
data: CollectionData,
info: ProjectInformation,
}>();
private returnValues = new Map<CollectionData, ICategoryCollection>();
public setUpReturnValue(
data: CollectionData,
collection: ICategoryCollection,
): CategoryCollectionParserSpy {
this.returnValues.set(data, collection);
return this;
}
public mockParser(): CategoryCollectionParserType {
return (data: CollectionData, info: IProjectInformation) => {
this.arguments.push({ data, info });
if (this.returnValues.has(data)) {
return this.returnValues.get(data);
}
// Get next OS with a unique OS so mock does not result in an invalid app due to duplicated OS
// collections.
const currentRun = this.arguments.length - 1;
const nextOs = getEnumValues(OperatingSystem)[currentRun];
return new CategoryCollectionStub().withOs(nextOs);
};
}
}