diff --git a/launchable/test_runners/karma.py b/launchable/test_runners/karma.py index 12ed811eb..ca07bbede 100644 --- a/launchable/test_runners/karma.py +++ b/launchable/test_runners/karma.py @@ -1,6 +1,3 @@ -# This runner only supports recording tests -# For subsetting, use 'ng' test runner instead -# It's possible to use 'karma' runner for recording, and 'ng' runner for subsetting, for the same test session import json from typing import Dict, Generator, List @@ -11,18 +8,6 @@ from . import launchable -@click.option('--with', '_with') -@launchable.subset -def subset(client, _with: str): - # TODO: implement the --with ng option - - # read lines as test file names - for t in client.stdin(): - client.test_path(t.rstrip("\n")) - - client.run() - - @click.argument('reports', required=True, nargs=-1) @launchable.record.tests def record_tests(client, reports): @@ -34,6 +19,30 @@ def record_tests(client, reports): client.run() +@click.option('--with', '_with', type=str, default=None, + help='Format output for specific test runner (e.g., "ng" for Angular CLI)') +@launchable.subset +def subset(client, _with): + """ + Usage: + find src -name "*.spec.ts" -o -name "*.spec.js" > test-list.txt + cat test-list.txt | launchable subset --target 10% karma + + # Output in ng test format + find src -name "*.spec.ts" | launchable subset --target 10% karma --with ng + """ + for t in client.stdin(): + path = t.strip() + if path: + client.test_path(path) + + if _with == 'ng': + client.formatter = lambda x: "--include={}".format(x[0]['name']) + client.separator = " " + + client.run() + + class JSONReportParser: """ Sample Karma report format: diff --git a/tests/data/karma/README.md b/tests/data/karma/README.md new file mode 100644 index 000000000..7c2039115 --- /dev/null +++ b/tests/data/karma/README.md @@ -0,0 +1,155 @@ +Karma +====== + +### Karma Project + +**Create project:** + +```bash +npm init -y +npm install --save-dev karma karma-jasmine jasmine-core karma-chrome-launcher karma-json-reporter +``` +The instructions are based on: +https://karma-runner.github.io/6.4/intro/installation.html + +**Generate `karma.conf.js`:** +```bash +npx karma init + +# Answer the prompts: +# - Framework: jasmine +# - Require.js: no +# - Browser: ChromeHeadless +# - Test files: +# - Files to exclude: +# - Watch files: no +``` + +**Add following to `karma.conf.js` while keeping the current settings:** +``` +module.exports = function (config) { + config.set({ + files: process.env.KARMA_FILES ? JSON.parse(process.env.KARMA_FILES) : [], + ... + plugins: [ + ... + require('karma-json-reporter') + ], + jsonReporter: { + outputFile: require('path').join(__dirname, 'test-results.json'), + stdout: false + }, + reporters: [..., 'json'] + }); +}; +``` + +**Create a test file:** +```bash +mkdir test + +cat > test/example.spec.js << 'EOF' +describe('Example', function() { + it('should pass', function() { + expect(true).toBe(true); + }); + + it('should add numbers', function() { + expect(1 + 1).toBe(2); + }); +}); +EOF +``` + +**Record session:** +```bash +git add . && git commit -m "Initial commit" +launchable record build --name ${BUILD_NAME} +launchable record session --build ${BUILD_NAME} > session.txt +``` + +**Run all tests:** +```bash +find test -name "*.spec.ts" -o -name "*.spec.js" > test_list.txt +cat test_list.txt +KARMA_FILES=$(cat test_list.txt | jq -R -s -c 'split("\n")[:-1]') +npx karma start --single-run +``` + +**Record tests:** +```bash +launchable record tests karma test-results.json +``` + +**Request subset:** +```bash +cat test_list.txt | launchable subset --target 25% karma > subset.txt +``` + +**Run subset of tests:** +```bash +KARMA_FILES=$(cat subset.txt | jq -R -s -c 'split("\n")[:-1]') +npx karma start --single-run +``` + +### Angular Project with Karma + +**Create project:** + +``` +ng new ng-karma-app --test-runner=karma +cd ng-karma-app +npm install --save-dev karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter jasmine-core karma-json-reporter @types/jasmine +ng test --no-watch --no-progress --browsers=ChromeHeadless +``` + +The instructions are based on: +- https://angular.dev/guide/testing/karma +- https://www.npmjs.com/package/karma-json-reporter + +**Generate `karma.conf.js`:** +``` +ng generate config karma +``` + +**Add following to `karma.conf.js` while keeping the current settings:** +``` +module.exports = function (config) { + config.set({ + ... + plugins: [ + ... + require('karma-json-reporter') + ], + jsonReporter: { + outputFile: require('path').join(__dirname, 'test-results.json'), + stdout: false + }, + reporters: [..., 'json'] + }); +}; +``` + +**Record session:** +``` +git add . && git commit -m "Initial commit" +launchable record build --name ${BUILD_NAME} +launchable record session --build ${BUILD_NAME} > session.txt +``` + +**Record tests:** +``` +ng test --no-watch --no-progress --browsers=ChromeHeadless +launchable record tests karma test-results.json +``` + +**Subset tests with **ng**:** +``` +ng test --list-tests | grep src > test_list.txt +cat test_list.txt | launchable subset --target 25% karma --with ng > subset.txt +``` + +**Run subset of tests** +``` +ng test --no-watch --no-progress --browsers=ChromeHeadless --include $(cat subset.txt) +``` diff --git a/tests/data/ng/subset_payload.json b/tests/data/karma/subset_payload.json similarity index 73% rename from tests/data/ng/subset_payload.json rename to tests/data/karma/subset_payload.json index d0b15b816..3d10b6085 100644 --- a/tests/data/ng/subset_payload.json +++ b/tests/data/karma/subset_payload.json @@ -4,13 +4,13 @@ { "type": "file", "name": "foo/bar/zot.spec.ts" } ], [ - { "type": "file", "name": "client-source/src/app/shared/other-test.spec.ts" } + { "type": "file", "name": "foo/bar/another.spec.ts" } ] ], - "testRunner": "ng", + "testRunner": "karma", "goal": {"type": "subset-by-percentage", "percentage": 0.1}, "ignoreNewTests": false, "session": { "id": "16" }, "getTestsFromGuess": false, "getTestsFromPreviousSessions": false -} +} \ No newline at end of file diff --git a/tests/test_runners/test_karma.py b/tests/test_runners/test_karma.py index c8163cb86..375561dcd 100644 --- a/tests/test_runners/test_karma.py +++ b/tests/test_runners/test_karma.py @@ -20,7 +20,7 @@ def test_record_tests_json(self): @responses.activate @mock.patch.dict(os.environ, {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token}) - def test_subset(self): + def test_subset_with_base(self): # emulate launchable record build write_build(self.build_name) @@ -28,3 +28,29 @@ def test_subset(self): os.getcwd(), 'karma', '--with', 'ng', input="a.ts\nb.ts") self.assert_success(result) self.assert_subset_payload('subset_result.json') + + @responses.activate + @mock.patch.dict(os.environ, + {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token}) + def test_subset(self): + write_build(self.build_name) + + subset_input = """foo/bar/zot.spec.ts +foo/bar/another.spec.ts +""" + result = self.cli('subset', '--target', '10%', 'karma', input=subset_input) + self.assert_success(result) + self.assert_subset_payload('subset_payload.json') + + @responses.activate + @mock.patch.dict(os.environ, + {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token}) + def test_subset_with_ng(self): + write_build(self.build_name) + + subset_input = """foo/bar/zot.spec.ts +foo/bar/another.spec.ts +""" + result = self.cli('subset', '--target', '10%', 'karma', '--with', 'ng', input=subset_input) + self.assert_success(result) + self.assert_subset_payload('subset_payload.json') diff --git a/tests/test_runners/test_ng.py b/tests/test_runners/test_ng.py deleted file mode 100644 index 36c090c29..000000000 --- a/tests/test_runners/test_ng.py +++ /dev/null @@ -1,22 +0,0 @@ -import os -from unittest import mock - -import responses # type: ignore - -from launchable.utils.session import write_build -from tests.cli_test_case import CliTestCase - - -class NgTest(CliTestCase): - @responses.activate - @mock.patch.dict(os.environ, - {"LAUNCHABLE_TOKEN": CliTestCase.launchable_token}) - def test_subset(self): - write_build(self.build_name) - - subset_input = """foo/bar/zot.spec.ts -client-source/src/app/shared/other-test.spec.ts -""" - result = self.cli('subset', '--target', '10%', 'ng', input=subset_input) - self.assert_success(result) - self.assert_subset_payload('subset_payload.json')