Merge pull request #2226 from spotify/rugvip/wintest
Make tests work on Windows and run as part of Windows master build
This commit is contained in:
@@ -38,6 +38,23 @@ jobs:
|
||||
- name: yarn install
|
||||
run: yarn install --frozen-lockfile
|
||||
# End of yarn setup
|
||||
# Tests are broken on Windows, disabled for now
|
||||
# - name: test
|
||||
# run: yarn lerna -- run test
|
||||
|
||||
- name: lint
|
||||
run: yarn lerna -- run lint
|
||||
|
||||
- name: type checking and declarations
|
||||
run: yarn tsc --incremental false
|
||||
|
||||
- name: verify type dependencies
|
||||
run: yarn lint:type-deps
|
||||
|
||||
- name: test
|
||||
run: yarn lerna -- run test
|
||||
|
||||
- name: Discord notification
|
||||
if: ${{ failure() }}
|
||||
uses: Ilshidur/action-discord@0.2.0
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
with:
|
||||
args: 'Windows master build failed https://github.com/{{GITHUB_REPOSITORY}}/actions/runs/{{GITHUB_RUN_ID}}'
|
||||
|
||||
@@ -106,7 +106,10 @@ export function findOwnRootDir(ownDir: string) {
|
||||
*/
|
||||
export function findPaths(searchDir: string): Paths {
|
||||
const ownDir = findOwnDir(searchDir);
|
||||
const targetDir = fs.realpathSync(process.cwd());
|
||||
// Drive letter can end up being lowercased here on Windows, bring back to uppercase for consistency
|
||||
const targetDir = fs
|
||||
.realpathSync(process.cwd())
|
||||
.replace(/^[a-z]:/, str => str.toUpperCase());
|
||||
|
||||
// Lazy load this as it will throw an error if we're not inside the Backstage repo.
|
||||
let ownRoot = '';
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('createPlugin', () => {
|
||||
await createTemporaryPluginFolder(tempDir);
|
||||
await movePlugin(tempDir, pluginDir, id);
|
||||
await expect(fs.pathExists(pluginDir)).resolves.toBe(true);
|
||||
expect(pluginDir).toMatch(`/plugins\/${id}`);
|
||||
expect(pluginDir).toMatch(path.join('', 'plugins', id));
|
||||
} finally {
|
||||
await del(tempDir, { force: true });
|
||||
await del(rootDir, { force: true });
|
||||
|
||||
@@ -39,7 +39,7 @@ const testPluginPackage = `${BACKSTAGE}/plugin-${testPluginName}`;
|
||||
const tempDir = path.join(os.tmpdir(), 'remove-plugin-test');
|
||||
|
||||
const removeEmptyLines = (file: string): string =>
|
||||
file.split('\n').filter(Boolean).join('\n');
|
||||
file.split(/\r?\n/).filter(Boolean).join('\n');
|
||||
|
||||
const createTestPackageFile = async (
|
||||
testFilePath: string,
|
||||
@@ -100,7 +100,7 @@ describe('removePlugin', () => {
|
||||
const packageFileContent = removeEmptyLines(
|
||||
fse.readFileSync(packageFilePath, 'utf8'),
|
||||
);
|
||||
expect(testFileContent === packageFileContent).toBe(true);
|
||||
expect(testFileContent).toBe(packageFileContent);
|
||||
} finally {
|
||||
fse.removeSync(testFilePath);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ describe('removePlugin', () => {
|
||||
const pluginsFileContent = removeEmptyLines(
|
||||
fse.readFileSync(pluginsFilePaths, 'utf8'),
|
||||
);
|
||||
expect(testFileContent === pluginsFileContent).toBe(true);
|
||||
expect(testFileContent).toBe(pluginsFileContent);
|
||||
} finally {
|
||||
fse.removeSync(testFilePath);
|
||||
}
|
||||
@@ -138,7 +138,7 @@ describe('removePlugin', () => {
|
||||
'test@gmail.com',
|
||||
]);
|
||||
await removePluginFromCodeOwners(testFilePath, testPluginName);
|
||||
expect(testFileContent === codeOwnersFileContent).toBeTruthy();
|
||||
expect(testFileContent).toBe(codeOwnersFileContent);
|
||||
} finally {
|
||||
if (fse.existsSync(testFilePath)) fse.removeSync(testFilePath);
|
||||
}
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/mock-fs": "^4.10.0",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/yup": "^0.28.2"
|
||||
"@types/yup": "^0.28.2",
|
||||
"mock-fs": "^4.13.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -14,46 +14,48 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const pathExists = jest.fn();
|
||||
|
||||
jest.mock('fs-extra', () => ({ pathExists }));
|
||||
|
||||
import mockFs from 'mock-fs';
|
||||
import { resolveStaticConfig } from './resolver';
|
||||
|
||||
function normalizePaths(paths: string[]) {
|
||||
return paths.map(p =>
|
||||
p
|
||||
.replace(/^[a-z]:/i, '')
|
||||
.split('\\')
|
||||
.join('/'),
|
||||
);
|
||||
}
|
||||
|
||||
describe('resolveStaticConfig', () => {
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should resolve no files for empty roots', async () => {
|
||||
mockFs({});
|
||||
const resolved = await resolveStaticConfig({
|
||||
env: 'development',
|
||||
rootPaths: [],
|
||||
});
|
||||
|
||||
expect(resolved).toEqual([]);
|
||||
expect(pathExists).not.toHaveBeenCalled();
|
||||
expect(normalizePaths(resolved)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should resolve a single app-config', async () => {
|
||||
pathExists.mockImplementation(async (path: string) =>
|
||||
['/repo/app-config.yaml'].includes(path),
|
||||
);
|
||||
mockFs({ '/repo/app-config.yaml': '' });
|
||||
const resolved = await resolveStaticConfig({
|
||||
env: 'development',
|
||||
rootPaths: ['/repo'],
|
||||
});
|
||||
|
||||
expect(resolved).toEqual(['/repo/app-config.yaml']);
|
||||
expect(pathExists).toHaveBeenCalledTimes(4);
|
||||
expect(normalizePaths(resolved)).toEqual(['/repo/app-config.yaml']);
|
||||
});
|
||||
|
||||
it('should resolve a app-configs in different directories', async () => {
|
||||
pathExists.mockImplementation(async (path: string) =>
|
||||
['/repo/app-config.yaml', '/repo/packages/a/app-config.yaml'].includes(
|
||||
path,
|
||||
),
|
||||
);
|
||||
mockFs({
|
||||
'/repo/app-config.yaml': '',
|
||||
'/repo/packages/a/app-config.yaml': '',
|
||||
});
|
||||
const resolved = await resolveStaticConfig({
|
||||
env: 'development',
|
||||
rootPaths: [
|
||||
@@ -64,53 +66,54 @@ describe('resolveStaticConfig', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(resolved).toEqual([
|
||||
expect(normalizePaths(resolved)).toEqual([
|
||||
'/repo/app-config.yaml',
|
||||
'/repo/packages/a/app-config.yaml',
|
||||
]);
|
||||
expect(pathExists).toHaveBeenCalledTimes(16);
|
||||
});
|
||||
|
||||
it('should resolve env and local configs', async () => {
|
||||
pathExists.mockImplementation(async (path: string) =>
|
||||
[
|
||||
'/repo/app-config.yaml',
|
||||
'/repo/app-config.local.yaml',
|
||||
'/repo/app-config.production.yaml',
|
||||
'/repo/app-config.production.local.yaml',
|
||||
'/repo/app-config.development.local.yaml',
|
||||
'/repo/packages/a/app-config.development.yaml',
|
||||
'/repo/packages/a/app-config.local.yaml',
|
||||
].includes(path),
|
||||
);
|
||||
mockFs({
|
||||
'/repo/app-config.yaml': '',
|
||||
'/repo/app-config.local.yaml': '',
|
||||
'/repo/app-config.production.yaml': '',
|
||||
'/repo/app-config.production.local.yaml': '',
|
||||
'/repo/app-config.development.local.yaml': '',
|
||||
'/repo/packages/a/app-config.development.yaml': '',
|
||||
'/repo/packages/a/app-config.local.yaml': '',
|
||||
});
|
||||
const resolved = await resolveStaticConfig({
|
||||
env: 'development',
|
||||
rootPaths: ['/repo', '/repo/packages/a'],
|
||||
});
|
||||
|
||||
expect(resolved).toEqual([
|
||||
expect(normalizePaths(resolved)).toEqual([
|
||||
'/repo/app-config.yaml',
|
||||
'/repo/app-config.local.yaml',
|
||||
'/repo/app-config.development.local.yaml',
|
||||
'/repo/packages/a/app-config.local.yaml',
|
||||
'/repo/packages/a/app-config.development.yaml',
|
||||
]);
|
||||
expect(pathExists).toHaveBeenCalledTimes(8);
|
||||
});
|
||||
|
||||
it('resolves suffixed configs in the correct order', async () => {
|
||||
pathExists.mockImplementation(async () => true);
|
||||
mockFs({
|
||||
'/repo/app-config.yaml': '',
|
||||
'/repo/app-config.local.yaml': '',
|
||||
'/repo/app-config.production.yaml': '',
|
||||
'/repo/app-config.production.local.yaml': '',
|
||||
});
|
||||
|
||||
const resolved = await resolveStaticConfig({
|
||||
env: 'production',
|
||||
rootPaths: ['/repo'],
|
||||
});
|
||||
|
||||
expect(resolved).toEqual([
|
||||
expect(normalizePaths(resolved)).toEqual([
|
||||
'/repo/app-config.yaml',
|
||||
'/repo/app-config.local.yaml',
|
||||
'/repo/app-config.production.yaml',
|
||||
'/repo/app-config.production.local.yaml',
|
||||
]);
|
||||
expect(pathExists).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,45 +15,30 @@
|
||||
*/
|
||||
|
||||
import { loadConfig } from './loader';
|
||||
|
||||
jest.mock('fs-extra', () => {
|
||||
const mockFiles: { [path in string]: string } = {
|
||||
'/root/app-config.yaml': `
|
||||
app:
|
||||
title: Example App
|
||||
sessionKey:
|
||||
$secret:
|
||||
file: secrets/session-key.txt
|
||||
`,
|
||||
'/root/app-config.development.yaml': `
|
||||
app:
|
||||
sessionKey: development-key
|
||||
`,
|
||||
'/root/secrets/session-key.txt': 'abc123',
|
||||
'/secret-port/app-config.yaml': `
|
||||
backend:
|
||||
listen:
|
||||
port:
|
||||
$secret:
|
||||
file: secrets/port.txt
|
||||
`,
|
||||
'/secret-port/secrets/port.txt': '12345',
|
||||
};
|
||||
|
||||
return {
|
||||
async readFile(path: string) {
|
||||
if (path in mockFiles) {
|
||||
return mockFiles[path];
|
||||
}
|
||||
throw new Error(`File not found, ${path}`);
|
||||
},
|
||||
async pathExists(path: string) {
|
||||
return path in mockFiles;
|
||||
},
|
||||
};
|
||||
});
|
||||
import mockFs from 'mock-fs';
|
||||
|
||||
describe('loadConfig', () => {
|
||||
beforeAll(() => {
|
||||
mockFs({
|
||||
'/root/app-config.yaml': `
|
||||
app:
|
||||
title: Example App
|
||||
sessionKey:
|
||||
$secret:
|
||||
file: secrets/session-key.txt
|
||||
`,
|
||||
'/root/app-config.development.yaml': `
|
||||
app:
|
||||
sessionKey: development-key
|
||||
`,
|
||||
'/root/secrets/session-key.txt': 'abc123',
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('loads config without secrets', async () => {
|
||||
await expect(
|
||||
loadConfig({
|
||||
|
||||
@@ -15,21 +15,21 @@
|
||||
*/
|
||||
|
||||
import { defaultConfigLoader } from './createApp';
|
||||
import { AppConfig } from '@backstage/config';
|
||||
|
||||
(process as any).env = { NODE_ENV: 'test' };
|
||||
const anyEnv = process.env as any;
|
||||
|
||||
describe('defaultConfigLoader', () => {
|
||||
afterEach(() => {
|
||||
delete process.env.APP_CONFIG;
|
||||
delete anyEnv.APP_CONFIG;
|
||||
});
|
||||
|
||||
it('loads static config', async () => {
|
||||
Object.defineProperty(process.env, 'APP_CONFIG', {
|
||||
configurable: true,
|
||||
value: [
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
{ data: { my: 'override-config' }, context: 'b' },
|
||||
] as AppConfig[],
|
||||
});
|
||||
anyEnv.APP_CONFIG = [
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
{ data: { my: 'override-config' }, context: 'b' },
|
||||
];
|
||||
|
||||
const configs = await defaultConfigLoader();
|
||||
expect(configs).toEqual([
|
||||
{ data: { my: 'config' }, context: 'a' },
|
||||
@@ -38,13 +38,11 @@ describe('defaultConfigLoader', () => {
|
||||
});
|
||||
|
||||
it('loads runtime config', async () => {
|
||||
Object.defineProperty(process.env, 'APP_CONFIG', {
|
||||
configurable: true,
|
||||
value: [
|
||||
{ data: { my: 'override-config' }, context: 'a' },
|
||||
{ data: { my: 'config' }, context: 'b' },
|
||||
] as AppConfig[],
|
||||
});
|
||||
anyEnv.APP_CONFIG = [
|
||||
{ data: { my: 'override-config' }, context: 'a' },
|
||||
{ data: { my: 'config' }, context: 'b' },
|
||||
];
|
||||
|
||||
const configs = await (defaultConfigLoader as any)(
|
||||
'{"my":"runtime-config"}',
|
||||
);
|
||||
@@ -62,20 +60,14 @@ describe('defaultConfigLoader', () => {
|
||||
});
|
||||
|
||||
it('fails to load invalid static config', async () => {
|
||||
Object.defineProperty(process.env, 'APP_CONFIG', {
|
||||
configurable: true,
|
||||
value: { my: 'invalid-config' } as any,
|
||||
});
|
||||
anyEnv.APP_CONFIG = { my: 'invalid-config' };
|
||||
await expect(defaultConfigLoader()).rejects.toThrow(
|
||||
'Static configuration has invalid format',
|
||||
);
|
||||
});
|
||||
|
||||
it('fails to load bad runtime config', async () => {
|
||||
Object.defineProperty(process.env, 'APP_CONFIG', {
|
||||
configurable: true,
|
||||
value: [{ data: { my: 'config' }, context: 'a' }] as AppConfig[],
|
||||
});
|
||||
anyEnv.APP_CONFIG = [{ data: { my: 'config' }, context: 'a' }];
|
||||
|
||||
await expect((defaultConfigLoader as any)('}')).rejects.toThrow(
|
||||
'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0',
|
||||
|
||||
@@ -47,21 +47,21 @@ describe('createRouter', () => {
|
||||
const response = await request(app).get('/index.html');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe('this is index.html\n');
|
||||
expect(response.text.trim()).toBe('this is index.html');
|
||||
});
|
||||
|
||||
it('returns other.html', async () => {
|
||||
const response = await request(app).get('/other.html');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe('this is other.html\n');
|
||||
expect(response.text.trim()).toBe('this is other.html');
|
||||
});
|
||||
|
||||
it('returns index.html if missing', async () => {
|
||||
const response = await request(app).get('/missing.html');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.text).toBe('this is index.html\n');
|
||||
expect(response.text.trim()).toBe('this is index.html');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,11 +83,11 @@ describe('createRouter with static fallback handler', () => {
|
||||
|
||||
const response1 = await request(app).get('/static/main.txt');
|
||||
expect(response1.status).toBe(200);
|
||||
expect(response1.text).toBe('this is main.txt\n');
|
||||
expect(response1.text.trim()).toBe('this is main.txt');
|
||||
|
||||
const response2 = await request(app).get('/static/test.txt');
|
||||
expect(response2.status).toBe(200);
|
||||
expect(response2.text).toBe('this is test.txt');
|
||||
expect(response2.text.trim()).toBe('this is test.txt');
|
||||
|
||||
const response3 = await request(app).get('/static/missing.txt');
|
||||
expect(response3.status).toBe(404);
|
||||
|
||||
@@ -96,6 +96,8 @@ describe('GitHubPreparer', () => {
|
||||
mockEntity.spec.path = './template/test/1/2/3';
|
||||
const response = await preparer.prepare(mockEntity);
|
||||
|
||||
expect(response).toMatch(new RegExp(/\/template\/test\/1\/2\/3$/));
|
||||
expect(response.split('\\').join('/')).toMatch(
|
||||
/\/template\/test\/1\/2\/3$/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,13 @@ import Docker from 'dockerode';
|
||||
import { runDockerContainer } from './helpers';
|
||||
|
||||
describe('helpers', () => {
|
||||
if (process.platform === 'win32') {
|
||||
// eslint-disable-next-line jest/no-focused-tests
|
||||
it.only('should skip tests on windows', () => {
|
||||
expect('test').not.toBe('run');
|
||||
});
|
||||
}
|
||||
|
||||
const mockDocker = new Docker() as jest.Mocked<Docker>;
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -17,6 +17,13 @@ import { DirectoryPreparer } from './dir';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { checkoutGitRepository } from './helpers';
|
||||
|
||||
function normalizePath(path: string) {
|
||||
return path
|
||||
.replace(/^[a-z]:/i, '')
|
||||
.split('\\')
|
||||
.join('/');
|
||||
}
|
||||
|
||||
jest.mock('./helpers', () => ({
|
||||
...jest.requireActual<{}>('./helpers'),
|
||||
checkoutGitRepository: jest.fn(() => '/tmp/backstage-repo/org/name/branch/'),
|
||||
@@ -47,7 +54,7 @@ describe('directory preparer', () => {
|
||||
'backstage.io/techdocs-ref': 'dir:./our-documentation',
|
||||
});
|
||||
|
||||
expect(await directoryPreparer.prepare(mockEntity)).toEqual(
|
||||
expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual(
|
||||
'/directory/our-documentation',
|
||||
);
|
||||
});
|
||||
@@ -61,7 +68,7 @@ describe('directory preparer', () => {
|
||||
'backstage.io/techdocs-ref': 'dir:/our-documentation/techdocs',
|
||||
});
|
||||
|
||||
expect(await directoryPreparer.prepare(mockEntity)).toEqual(
|
||||
expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual(
|
||||
'/our-documentation/techdocs',
|
||||
);
|
||||
});
|
||||
@@ -75,7 +82,7 @@ describe('directory preparer', () => {
|
||||
'backstage.io/techdocs-ref': 'dir:./docs',
|
||||
});
|
||||
|
||||
expect(await directoryPreparer.prepare(mockEntity)).toEqual(
|
||||
expect(normalizePath(await directoryPreparer.prepare(mockEntity))).toEqual(
|
||||
'/tmp/backstage-repo/org/name/branch/docs',
|
||||
);
|
||||
expect(checkoutGitRepository).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -18,6 +18,13 @@ import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { GithubPreparer } from './github';
|
||||
import { checkoutGithubRepository } from './helpers';
|
||||
|
||||
function normalizePath(path: string) {
|
||||
return path
|
||||
.replace(/^[a-z]:/i, '')
|
||||
.split('\\')
|
||||
.join('/');
|
||||
}
|
||||
|
||||
jest.mock('./helpers', () => ({
|
||||
...jest.requireActual<{}>('./helpers'),
|
||||
checkoutGithubRepository: jest.fn(
|
||||
@@ -51,7 +58,7 @@ describe('github preparer', () => {
|
||||
|
||||
const tempDocsPath = await preparer.prepare(mockEntity);
|
||||
expect(checkoutGithubRepository).toHaveBeenCalledTimes(1);
|
||||
expect(tempDocsPath).toEqual(
|
||||
expect(normalizePath(tempDocsPath)).toEqual(
|
||||
'/tmp/backstage-repo/org/name/branch/plugins/techdocs-backend/examples/documented-component',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -4496,6 +4496,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mock-fs@^4.10.0":
|
||||
version "4.10.0"
|
||||
resolved "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b"
|
||||
integrity sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/morgan@^1.9.0":
|
||||
version "1.9.1"
|
||||
resolved "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.1.tgz#6457872df95647c1dbc6b3741e8146b71ece74bf"
|
||||
@@ -14793,6 +14800,11 @@ mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdir
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
mock-fs@^4.13.0:
|
||||
version "4.13.0"
|
||||
resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz#31c02263673ec3789f90eb7b6963676aa407a598"
|
||||
integrity sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA==
|
||||
|
||||
modify-values@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||
|
||||
Reference in New Issue
Block a user