Skip to content

Implemented TestCafe configuration file#3147

Merged
miherlosev merged 26 commits intoDevExpress:masterfrom
miherlosev:i3131
Dec 24, 2018
Merged

Implemented TestCafe configuration file#3147
miherlosev merged 26 commits intoDevExpress:masterfrom
miherlosev:i3131

Conversation

@miherlosev
Copy link
Copy Markdown
Collaborator

No description provided.

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@miherlosev
Copy link
Copy Markdown
Collaborator Author

@testcafe-build-bot retest

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@miherlosev
Copy link
Copy Markdown
Collaborator Author

@kirovboris @AndreyBelym FPR

@miherlosev miherlosev changed the title [WIP] Implemented TestCafe configuration file Implemented TestCafe configuration file Dec 4, 2018
Copy link
Copy Markdown
Contributor

@kirovboris kirovboris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/r-


_getReporterPlugins () {
async _ensureOutStream (outStream) {
if (typeof outStream === 'string') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (typeof outStream !== 'string') 
    return outStream;

const fullReporterOutputPath = resolvePathRelativelyCwd(outStream);

await makeDir(path.dirname(fullReporterOutputPath));

return  fs.createWriteStream(fullReporterOutputPath);


const DEBUG_LOGGER = debug('testcafe:runner');

const DEFAULT_TIMEOUT = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use the following notation

const DEFAULT_TIMEOUT = {
    selector:  10000,
    assertion: 3000,
    pageLoad: 3000
};

}

static _validateReporterOutput (obj) {
if (obj !== void 0 &&
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's refactor it:

const <varName> = ...

if(<varName>)
    throw new GeneralError(...);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


browsers (...browsers) {
this.bootstrapper.browsers = this.bootstrapper.browsers.concat(flatten(browsers));
if (this.apiMethodWasCalled.browsers)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we could refactor FlagList to throw an exception in a field setter? Thus we won't need all the if(this.apiMethodWasCalled) conditions.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with you. FlagList is a simple concept. Don't need to extend it with error generation.

return parsedOptions;
}

export async function ensureOptionValue (optionName, optionValue) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It had better use pure functions everywhere it's possible.

export async function ensureOptionValue (optionName, optionValue) {
    const convertedValue = convertToBestFitType(optionValue);
	
    return ensureFileOptionValue(optionName, convertedValue);     
}

const CONFIGURATION_FILENAME = '.testcaferc.json';

const ERR_READ_CONFIG_FILE = 'An error is occurred during reading the configuration filename.';
const ERR_CONFIG_FILE_IS_NOT_WELLFORMATTED = 'Configuration filename is not well-formatted.';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this message doesn't reflect that json is can't be parsed. Probably it suppose well-formed, but anyway it should describe expected format (json is not well-formed).

Copy link
Copy Markdown
Collaborator Author

@miherlosev miherlosev Dec 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. In this case, I propose to use this message:

Configuration file is not well-formed.
Failed to parse '.testcaferc.json' data.
.testcaferc.json must be actual JSON.

This message is similar with
image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but anyway it should be checked by tech writers.


const CONFIGURATION_FILENAME = '.testcaferc.json';

const ERR_READ_CONFIG_FILE = 'An error is occurred during reading the configuration filename.';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a GeneralError, isn't it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an error about problem with reading configuration file. Not just reading file.
We will convert ERR_READ_CONFIG_FILE to the GeneralError when another reading file code will be appeared.

});

if (overridenOptions.length)
console.log(`${overridenOptions.map(option => `"${option}"`).join(', ')} option${overridenOptions.length > 1 ? 's' : ''} from configuration file will be ignored.`); // eslint-disable-line no-console
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep all strings in constants.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a calculated string. Its value depends on the overridenOptions array, declared in the function.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can store it in the notifications/warning-messages and then use renderWarning to render a string for output.

Copy link
Copy Markdown
Collaborator Author

@miherlosev miherlosev Dec 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Then it will be a warning message instead of the log message as we discussed earlier.
@kirovboris

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndreyBelym, I don't think the outputting this message as a warning is a good idea. It's just a log message.

hostname: this.getOption('hostname'),
port1: this.getOption('port1'),
port2: this.getOption('port2'),
options: {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok, that eslint doens't throw error here?

hostname: this.getOption('hostname'),
port1:    this.getOption('port1'),
port2:    this.getOption('port2'),

options: {
     ssl:             this.getOption('ssl'),
     developmentMode: this.getOption('developmentMode'),
     retryTestPages:  !!this.getOption('retryTestPages')
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote the options property without an empty line.
eslint will generate an error if I add the empty line.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you just had to remove an extra space in options: {?

We already use such formatting:

https://github.com/DevExpress/testcafe/blob/master/test/functional/config.js#L30

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Let it be.

}

try {
const optionsObj = JSON.parse(configurationFileContent);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to use JSON5 rather than standard JSON: it has relatively relaxed grammar and allows code comments which can be quite handy sometimes. AFAIK many popular tools use it.

Also, there is a trend recently to use JS files that export configuration object instead of dealing with all the JSON-related shenanigans. Thus, you can have arbitrary configuration logic that depends on environment, for example.

Copy link
Copy Markdown
Collaborator Author

@miherlosev miherlosev Dec 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I glad to see you @inikulin . We've discussed about other configuration file formats (json, js and yml) and decided to support only the JSON format in the first implementation.

Thank you for the provided information about JSON5. But I think we will use current JSON implementation until there are problems with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this idea and I have nothing to add to the mentioned pros.
@inikulin, I glad to see you too 😄.

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@miherlosev
Copy link
Copy Markdown
Collaborator Author

@testcafe-build-bot retest

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@miherlosev
Copy link
Copy Markdown
Collaborator Author

FPR

@miherlosev
Copy link
Copy Markdown
Collaborator Author

ping @kirovboris

Copy link
Copy Markdown
Contributor

@kirovboris kirovboris left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/r-
just minor remarks

mergeOptions (options) {
const overridenOptions = [];

Object.entries(options).map(item => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

item -> ([key, value])
Here and in the other usages of Object.entries

@@ -0,0 +1,11 @@
import Configuration from './index';

export default async function initConfiguration (options) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's should be just a method of Configuration class

const configuration = new Configuration(...);

await configuration.init(...);

@testcafe-build-bot
Copy link
Copy Markdown
Collaborator

@miherlosev
Copy link
Copy Markdown
Collaborator Author

FPR

@miherlosev miherlosev merged commit b60792b into DevExpress:master Dec 24, 2018
@miherlosev miherlosev deleted the i3131 branch December 25, 2018 09:51
kirovboris pushed a commit to kirovboris/testcafe-phoenix that referenced this pull request Dec 18, 2019
* initial implementation

* .filter changes

* fix unit tests

* ensureArray option for src and browsers

* .reporter changes

* tests

* small refactoring

* small refactoring 2

* functional tests

* fix reporter tests

* remove .only

* fix test

* test for reporters

* fix test for concurrency

* fix eslint

* resolvePathRelativelyCwd

* minor refactoring

* fix test

* fix remarks

* fix tests

* fix tests again

* corrector's changes

* move concurrency to ops.concurrency

* fix remarks

* fix remarks 2

* remove additional spaces between parameters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants