Skip to content

Implement run with zero config (#1141) #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/bin/intern.ts
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@ getConfig()
console.log(getConfigDescription(config));
} else {
if (!file) {
console.warn('No config file was loaded');
console.warn('No config file was loaded, using default one...');
}

intern.configure({ reporters: 'runner' });
@@ -29,9 +29,9 @@ getConfig()
if (
intern.environment === 'browser' &&
((intern.config.suites &&
intern.config.suites.some(pattern => pattern.endsWith('.ts'))) ||
intern.config.suites.some((pattern) => pattern.endsWith('.ts'))) ||
(intern.config.plugins &&
intern.config.plugins.some(plugin =>
intern.config.plugins.some((plugin) =>
plugin.script.endsWith('.ts')
)))
) {
@@ -42,7 +42,7 @@ getConfig()
return intern.run();
}
})
.catch(error => {
.catch((error) => {
// If intern wasn't initialized, then this error won't have been
// reported
if (!error.reported) {
@@ -76,7 +76,7 @@ function printHelp(config: any, file?: string) {

const internConfig = (<any>intern)._config;
const opts = Object.keys(internConfig)
.map(key => {
.map((key) => {
return { name: key, value: JSON.stringify(internConfig[key]) };
})
.sort((a, b) => {
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@
}

if (!result.file) {
console.warn('No config file was found');
console.warn('No config file was found, using default one...');
}

// Add the HTML and console reporters to the default
37 changes: 25 additions & 12 deletions src/lib/browser/util.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { request, CancellablePromise, global } from '@theintern/common';

import {
createDefaultConfig,
getBasePath,
loadConfig,
parseArgs,
splitConfigPath
splitConfigPath,
} from '../common/util';

/**
@@ -30,31 +31,32 @@ export function getConfig(file?: string) {
// If no config parameter was provided, try 'intern.json'. If that file
// doesn't exist, just return the args
file = resolvePath('intern.json', configBase);
load = loadConfig(file, loadText, args).catch(error => {
load = loadConfig(file, loadText, args).catch((error) => {
if (error.message.indexOf('Request failed') === 0) {
// The file wasn't found, clear the file name
file = undefined;
return args;
return getSuitesExtension(configBase).then((extension) =>
createDefaultConfig(args, extension)
);
}
throw error;
});
}

return load
.then(config => {
.then((config) => {
// If a basePath wasn't set in the config or via a query arg, and we
// have a config file path, use that.
if (file) {
config.basePath = getBasePath(
file,
config.basePath,
path => path[0] === '/',
(path) => path[0] === '/',
'/'
);
}
return config;
})
.then(config => ({ config, file }));
.then((config) => ({ config, file }));
}

/**
@@ -138,17 +140,18 @@ export type Url = {
};
export function parseUrl(url: string): Url | undefined {
if (url) {
const match = /^(([^:\/?#]+):)?(\/\/(([^:\/?#]*)(:(\d+))?))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(
url
);
const match =
/^(([^:\/?#]+):)?(\/\/(([^:\/?#]*)(:(\d+))?))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(
url
);
if (match) {
return {
protocol: match[2],
hostname: match[5],
port: match[7],
path: match[8],
query: match[10],
hash: match[12]
hash: match[12],
};
}
}
@@ -158,7 +161,7 @@ export function parseUrl(url: string): Url | undefined {
* Load a text resource
*/
function loadText(path: string): CancellablePromise<any> {
return request(path).then(response => {
return request(path).then((response) => {
if (!response.ok) {
throw new Error('Request failed: ' + response.status);
}
@@ -191,3 +194,13 @@ function resolvePath(path: string, basePath: string) {

return basePathParts.join('/');
}

async function getSuitesExtension(basePath: string) {
return existsAsync(resolvePath('tsconfig.json', basePath)).then(
(tsconfigExists) => (tsconfigExists ? 'ts' : 'js')
);
}

async function existsAsync(filePath: string) {
return request(filePath, { method: 'HEAD' }).then((response) => response.ok);
}
12 changes: 12 additions & 0 deletions src/lib/common/util.ts
Original file line number Diff line number Diff line change
@@ -799,6 +799,18 @@ export function setOption(
}
}

/**
* Create a default config to make zero-config running possible
*/
export function createDefaultConfig(baseConfig: any, suitesExtension: string) {
let config = Object.assign({}, baseConfig);
config.suites = [
`tests/**/*.${suitesExtension}`,
`src/**/*.spec.${suitesExtension}`,
];
return config;
}

/**
* Split a config path into a file name and a child config name.
*
25 changes: 15 additions & 10 deletions src/lib/node/util.ts
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import {
join,
normalize,
resolve,
sep
sep,
} from 'path';
import { parse } from 'shell-quote';
import { RawSourceMap } from 'source-map';
@@ -16,10 +16,11 @@ import { Task, CancellablePromise } from '@theintern/common';

import process from './process';
import {
createDefaultConfig,
getBasePath,
loadConfig,
parseArgs,
splitConfigPath
splitConfigPath,
} from '../common/util';
/**
* Expand a list of glob patterns into a flat file list. Patterns may be simple
@@ -50,10 +51,10 @@ export function expandFiles(patterns?: string[] | string) {
}

const allPaths = includes
.map(pattern => glob(pattern, { ignore: excludes }))
.map((pattern) => glob(pattern, { ignore: excludes }))
.reduce((allFiles, files) => allFiles.concat(files), paths);
const uniquePaths: { [name: string]: boolean } = {};
allPaths.forEach(path => (uniquePaths[path] = true));
allPaths.forEach((path) => (uniquePaths[path] = true));

return Object.keys(uniquePaths);
}
@@ -116,23 +117,22 @@ export function getConfig(
(error: NodeJS.ErrnoException) => {
if (error.code === 'ENOENT') {
file = undefined;
return args;
return createDefaultConfig(args, getSuitesExtension());
}
throw error;
}
);
}

return load
.then(config => {
.then((config) => {
// If a basePath wasn't set in the config or via a query arg, and we
// have a config file path, use that.
if (file) {
config.basePath = getBasePath(file, config.basePath, isAbsolute, sep);
}
return config;
})
.then(config => ({ config, file }));
.then((config) => ({ config, file }));
}

/**
@@ -213,7 +213,7 @@ export function transpileSource(filename: string, code: string) {
{
_compile(source: string) {
code = source;
}
},
} as any,
filename
);
@@ -222,4 +222,9 @@ export function transpileSource(filename: string, code: string) {
}

// Regex for matching sourceMappingUrl comments
const sourceMapRegEx = /^(?:\/{2}[#@]{1,2}|\/\*)\s+sourceMappingURL\s*=\s*(data:(?:[^;]+;)+base64,)?(\S+)/;
const sourceMapRegEx =
/^(?:\/{2}[#@]{1,2}|\/\*)\s+sourceMappingURL\s*=\s*(data:(?:[^;]+;)+base64,)?(\S+)/;

function getSuitesExtension() {
return existsSync(resolve('tsconfig.json')) ? 'ts' : 'js';
}