Заставьте grunt выполнить задачу после выполнения подзадачи - PullRequest
1 голос
/ 04 ноября 2019

Проблема

У меня серьезная задача запустить автоматические тесты. Логика такова:

clean .tmp folder -> найти нужный файл данных в формате .xlx -> преобразовать файл в JSON в папке .tmp -> запустить тестирование для преобразованных файлов

Работало нормально, пока файл данных не стал очень большим по размеру, и тесты начинались до того, как подзадача, преобразующая файл, заканчивалась

Вопрос

Как заставить grunt ждать, пока подзадачаготово, не больше / не меньше?

Что я пробовал до сих пор

Я пробовал несколько способов сделать задачу convert-data-sheet асинхронной без какой-либо удачи.

1. Очевидно, что первое, что я попробовал, было это

// register task for converting data sheet
grunt.registerTask('convert-data-sheet', 'task for converting xslx file into json', function(product, tenant, environment, codeBase) {

   let done = this.async();
   /*
    Some code here, not essential to the question
    */
    // run conversion for each sheet
    for (let i = 0; i < sheetTabs.length; i++) {
        dst = path.resolve(__dirname, './protractor/.tmp_files/test_data', `${sheetTabs[i]}.json`);
        options.sheet = (i + 1).toString();
        convertExcel(src, dst, options);
    }
    done();
});

// register a task for ui tests
grunt.registerTask('e2e', 'task for protractor tests', function() {
    let done = this.async();
    grunt.initConfig({
        /**
         * Long and boring initConfig object
         */
    });

    // clean .tmp directory
    grunt.task.run('clean:tmpFiles');

    // generate data json files and other files
    grunt.task.run([`convert-data-sheet:${parameters.product}:${parameters.tenant}:${parameters.environment}:${parameters.codeBase}`, 'bake:protractorConfigs']);

    grunt.task.run('protractor:configFile');
    done();
});

Он все еще пытается запустить Protractor до создания файлов данных

2. Вот мой текущий код, который просто зависает в течение указанного периода времени

// register task for converting data sheet
grunt.registerTask('convert-data-sheet', 'task for converting xslx file into json', function(product, tenant, environment, codeBase) {
    /*
    Some code here, not essential to the question
    */
    // run conversion for each sheet
    for (let i = 0; i < sheetTabs.length; i++) {
        dst = path.resolve(__dirname, './protractor/.tmp_files/test_data', `${sheetTabs[i]}.json`);
        options.sheet = (i + 1).toString();
        convertExcel(src, dst, options);
    }
});

// register a task for ui tests
grunt.registerTask('e2e', 'task for protractor tests', function() {

    let done = this.async();

    grunt.initConfig({
        /**
         * Long and boring initConfig object
         */
    });

    // clean .tmp directory
    grunt.task.run('clean:tmpFiles');

    // generate data json files and other files
    grunt.task.run([`convert-data-sheet:${parameters.product}:${parameters.tenant}:${parameters.environment}:${parameters.codeBase}`, 'bake:protractorConfigs']);

    // run protractor with a delay 2000ms to let previous tasks finish
    setTimeout(function() {
        grunt.task.run('protractor:configFile');
        done();
    }, 2000);
});

Но оказывается, что даже текущий код не выполняет то, что ожидается. Он просто зависает на 2 секунды, ничего не делая, ДАЖЕ ДО начала преобразования данных. Так что из логов я вижу - ворчание началось -> зависание 2000 -> конвертировать -> запустить транспортир

Ответы [ 2 ]

0 голосов
/ 13 ноября 2019

Решение

// register task for converting data sheet
grunt.registerTask('convert-data-sheet', 'task for converting xslx file into json', function(product, tenant, environment, codeBase) {

   let done = this.async();
   /*
    Some code here, not essential to the question
    */
    mkdirp(path.resolve(__dirname, './protractor/.tmp_files/test_data'), function(err) {

        // run conversion for each sheet
        for (let i = 0; i < sheetTabs.length; i++) {
            dst = path.resolve(__dirname, './protractor/.tmp_files/test_data', `${sheetTabs[i]}.json`);
            options.sheet = (i + 1).toString();
            convertExcel(src, dst, options,
                (err, data) => {
                    if (i === (sheetTabs.length - 1)) {
                        done();
                    }
                }
            );
        }
    });
});

// register a task for ui tests
grunt.registerTask('e2e', 'task for protractor tests', function() {

    grunt.initConfig({
        /**
         * Long and boring initConfig object
         */
    });

    // clean .tmp directory
    grunt.task.run('clean:tmpFiles');

    // generate data json files and other files
    grunt.task.run([`convert-data-sheet:${parameters.product}:${parameters.tenant}:${parameters.environment}:${parameters.codeBase}`, 'bake:protractorConfigs']);

    grunt.task.run('protractor:configFile');

});

Что особенного в этом подходе

Я не совсем понял, что происходит с синхронизированным выполнением задачи.

Как мы все знаем, этот сценарий

console.log(1)
setTimeout(function(){console.log(2)}, 1000)
console.log(3)

приводит к

1
3
2

, потому что JS просто планирует setTimeout и пока он ожидает 1000 мс,это продолжается со сценарием

В моем случае вы можете думать о convertExcel с setTimeout. Таким образом, JS запланировал преобразование, а затем немедленно вызвал done(), поэтому Грант не стал ждать, пока процесс фактически завершится.

К счастью, модуль excel-as-json имеет опцию для передачи функции обратного вызова, что я и сделал. Поэтому все, что мне нужно сделать, это вызвать done(), когда преобразование вкладки последнего листа было фактически выполнено.

Единственная проблема, с которой я столкнулся после исправления, была эта ошибка Fatal error: ENOENT: no such file or directory, mkdir ..., которая была решена простымmkdir в игру

Важно

Это все еще не идеально, потому что если у меня есть 10 листов в моем файле Excel, и пятый самый большой, но я жду толькопоследний, это не сработает. Но пока я знаю, как это исправить, я буду делать это при необходимости

0 голосов
/ 13 ноября 2019

Я бы посоветовал вам иметь семафорную переменную

https://en.m.wikipedia.org/wiki/Semaphore_(programming)

. В этом случае создайте глобальную переменную с именем dataLoaded и установите ее на false.

В вашем первом подзадаче установите это значение на true, как только оно будет выполнено.

После этого ваша вторая задача может проверить значение dataLoaded при запуске и, если оно не установлено на true, вызвать setTimeout для себя, чтобы немного подождать и повторить попытку.

function foo() {
  if (!dataLoaded) {
    setTimeout(foo, 50)
    return
  }

  ...
}
...