diff --git a/.github/workflows/jasmine.yaml b/.github/workflows/jasmine.yaml new file mode 100644 index 00000000..a95f0c29 --- /dev/null +++ b/.github/workflows/jasmine.yaml @@ -0,0 +1,53 @@ +name: jasmine + +on: + push: + branches: + - master + paths: + - "jasmine/**" + pull_request: + paths: + - "jasmine/**" + - .github/workflows/jasmine.yaml + +env: + LAUNCHABLE_TOKEN: ${{ secrets.LAUNCHABLE_TOKEN_JASMINE }} + LAUNCHABLE_DEBUG: 1 + LAUNCHABLE_REPORT_ERROR: 1 + +jobs: + tests: + runs-on: ubuntu-latest + defaults: + run: + working-directory: jasmine + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + - name: Install Launchable CLI + run: pip install launchable + - name: Launchable verify + run: launchable verify + - name: Install dependencies + run: npm install + - name: Record build + run: launchable record build --name "$GITHUB_RUN_ID" + - name: Save all tests to a file + run: find spec/jasmine_examples -type f > test_list.txt + - name: Run all tests + run: npx jasmine $(cat test_list.txt) + - name: Record tests + run: launchable record tests --base $(pwd) jasmine jasmine-report.json + - name: Request subset + run: cat test_list.txt | launchable subset --target 25% jasmine > subset.txt + - name: Run subset of tests + run: npx jasmine $(cat subset.txt) \ No newline at end of file diff --git a/jasmine/.gitignore b/jasmine/.gitignore new file mode 100644 index 00000000..c8d38495 --- /dev/null +++ b/jasmine/.gitignore @@ -0,0 +1,5 @@ +node_modules +.launchable +.python-version +jasmine-report.json +*.txt \ No newline at end of file diff --git a/jasmine/README.md b/jasmine/README.md new file mode 100644 index 00000000..ed305890 --- /dev/null +++ b/jasmine/README.md @@ -0,0 +1,66 @@ +Jasmine Example Project +===================== + +#### Project Structure + +``` +jasmine +└── spec + ├── helpers + │ └── jasmine-json-test-reporter.js + │ + └── jasmine_examples + ├── PlayerSpec.js + └── SongSpec.js +``` + +NOTE: The tests in SongSpec.js are set to fail intentionally. + +#### To Build the Project + +``` +npm install +``` + +#### To Record Tests + +Create test session: + +``` +BUILD_NAME=jasmine_build +launchable record build --name ${BUILD_NAME} +launchable record session --build ${BUILD_NAME} > session.txt +``` + +Find and write all tests to a file: + +``` +find spec/jasmine_examples -type f > test_list.txt +``` + +To run all tests: + +``` +npx jasmine $(cat test_list.txt) +``` + +This creates `jasmine-report.json` in the project root. + +Then to record the tests: + +``` +launchable record tests --base $(pwd) jasmine jasmine-report.json +``` + +To request subset: + +``` +cat test_list.txt | launchable subset --target 25% jasmine > subset.txt +``` + +To run subset of tests: + +``` +npx jasmine $(cat subset.txt) +``` + diff --git a/jasmine/lib/jasmine_examples/Player.js b/jasmine/lib/jasmine_examples/Player.js new file mode 100644 index 00000000..d0a2fcf3 --- /dev/null +++ b/jasmine/lib/jasmine_examples/Player.js @@ -0,0 +1,24 @@ +class Player { + play(song) { + this.currentlyPlayingSong = song; + this.isPlaying = true; + } + + pause() { + this.isPlaying = false; + } + + resume() { + if (this.isPlaying) { + throw new Error('song is already playing'); + } + + this.isPlaying = true; + } + + makeFavorite() { + this.currentlyPlayingSong.persistFavoriteStatus(true); + } +} + +module.exports = Player; diff --git a/jasmine/lib/jasmine_examples/Song.js b/jasmine/lib/jasmine_examples/Song.js new file mode 100644 index 00000000..a996a3f0 --- /dev/null +++ b/jasmine/lib/jasmine_examples/Song.js @@ -0,0 +1,8 @@ +class Song { + persistFavoriteStatus(value) { + // something complicated + throw new Error('not yet implemented'); + } +} + +module.exports = Song; diff --git a/jasmine/package.json b/jasmine/package.json new file mode 100644 index 00000000..b2c1dc0a --- /dev/null +++ b/jasmine/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "glob": "^11.0.3", + "jasmine": "^5.12.0", + "jasmine-json-test-reporter": "^1.0.0-beta" + } +} diff --git a/jasmine/spec/helpers/jasmine-json-test-reporter.js b/jasmine/spec/helpers/jasmine-json-test-reporter.js new file mode 100644 index 00000000..4de6ebf6 --- /dev/null +++ b/jasmine/spec/helpers/jasmine-json-test-reporter.js @@ -0,0 +1,4 @@ +var JSONReporter = require('jasmine-json-test-reporter'); +jasmine.getEnv().addReporter(new JSONReporter({ + file: 'jasmine-report.json' +})); diff --git a/jasmine/spec/helpers/jasmine_examples/SpecHelper.js b/jasmine/spec/helpers/jasmine_examples/SpecHelper.js new file mode 100644 index 00000000..ab35b345 --- /dev/null +++ b/jasmine/spec/helpers/jasmine_examples/SpecHelper.js @@ -0,0 +1,15 @@ +beforeEach(function () { + jasmine.addMatchers({ + toBePlaying: function () { + return { + compare: function (actual, expected) { + const player = actual; + + return { + pass: player.currentlyPlayingSong === expected && player.isPlaying + }; + } + }; + } + }); +}); diff --git a/jasmine/spec/jasmine_examples/PlayerSpec.js b/jasmine/spec/jasmine_examples/PlayerSpec.js new file mode 100644 index 00000000..19974667 --- /dev/null +++ b/jasmine/spec/jasmine_examples/PlayerSpec.js @@ -0,0 +1,61 @@ +const Player = require('../../lib/jasmine_examples/Player'); +const Song = require('../../lib/jasmine_examples/Song'); + +describe('Player', function () { + let player; + let song; + + beforeEach(function () { + player = new Player(); + song = new Song(); + }); + + it('should be able to play a Song', function () { + player.play(song); + expect(player.currentlyPlayingSong).toEqual(song); + + // demonstrates use of custom matcher + expect(player).toBePlaying(song); + }); + + describe('when song has been paused', function () { + beforeEach(function () { + player.play(song); + player.pause(); + }); + + it('should indicate that the song is currently paused', function () { + expect(player.isPlaying).toBeFalsy(); + + // demonstrates use of 'not' with a custom matcher + expect(player).not.toBePlaying(song); + }); + + it('should be possible to resume', function () { + player.resume(); + expect(player.isPlaying).toBeTruthy(); + expect(player.currentlyPlayingSong).toEqual(song); + }); + }); + + // demonstrates use of spies to intercept and test method calls + it('tells the current song if the user has made it a favorite', function () { + spyOn(song, 'persistFavoriteStatus'); + + player.play(song); + player.makeFavorite(); + + expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); + }); + + //demonstrates use of expected exceptions + describe('#resume', function () { + it('should throw an exception if song is already playing', function () { + player.play(song); + + expect(function () { + player.resume(); + }).toThrowError('song is already playing'); + }); + }); +}); diff --git a/jasmine/spec/jasmine_examples/SongSpec.js b/jasmine/spec/jasmine_examples/SongSpec.js new file mode 100644 index 00000000..1a31e1c3 --- /dev/null +++ b/jasmine/spec/jasmine_examples/SongSpec.js @@ -0,0 +1,17 @@ +const Player = require('../../lib/jasmine_examples/Player'); +const Song = require('../../lib/jasmine_examples/Song'); + +describe('Song', function () { + let player; + let song; + + beforeEach(function () { + player = new Player(); + song = new Song(); + }); + + it('should be able to play by player', function () { + player.play(song); + expect(player.isPlaying).toBeTruthy(); + }); +}); diff --git a/jasmine/spec/support/jasmine.mjs b/jasmine/spec/support/jasmine.mjs new file mode 100644 index 00000000..8e183ff0 --- /dev/null +++ b/jasmine/spec/support/jasmine.mjs @@ -0,0 +1,14 @@ +export default { + spec_dir: "spec", + spec_files: [ + "**/*[sS]pec.?(m)js" + ], + helpers: [ + "helpers/**/*.?(m)js" + ], + env: { + stopSpecOnExpectationFailure: false, + random: true, + forbidDuplicateNames: true + } +}