diff --git a/.github/workflows/publish-node.js.yml b/.github/workflows/publish-node.js.yml index 9fcfec8..eccdffb 100644 --- a/.github/workflows/publish-node.js.yml +++ b/.github/workflows/publish-node.js.yml @@ -1,33 +1,35 @@ -# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node +# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions - -name: Publish +name: Publish npm Package on: - release: - types: [published] -jobs: - publish: + push: + branches: + - master + - main +jobs: + publish-npm: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - ref: ${{ github.event.release.target_commitish }} - - uses: actions/setup-node@v2 - with: - node-version: 16 - cache: 'npm' - - run: git config --global user.name "GitHub CD bot" - - run: git config --global user.email "github-cd-bot@example.com" - - run: npm version ${{ github.event.release.tag_name }} - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - - # push the version changes to GitHub - - run: git push - env: - # The secret is passed automatically. Nothing to configure. - github-token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + registry-url: https://registry.npmjs.org/ + - run: git config --global user.name "GitHub CD bot" + - run: git config --global user.email "github-cd-bot@example.com" + - name: Install deps + run: npm i + - name: Compile code + run: npm run build + - name: publish package + run: npx semantic-release + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # push the version changes to GitHub + - run: git add package.json && git commit -m'update version' && git push + env: + # The secret is passed automatically. Nothing to configure. + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1325408 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Acceptance Tests + +on: + push: + branches: + - master + - main + pull_request: + branches: + - '**' + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [20.x] + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - name: Run tests + run: | + npm i --force && npm run test diff --git a/.gitignore b/.gitignore index 25c8fdb..63fb8f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +.idea +dist diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..3cd1614 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + roots: [''], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + moduleDirectories: ['node_modules', '.'] +} diff --git a/package.json b/package.json index 375e8be..19e8387 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,14 @@ "name": "@codeceptjs/helper", "version": "2.0.1", "description": "Base class for CodeceptJS helpers", - "main": "helper.js", + "main": "dist/index.js", "files": [ - "helper.js" + "dist/*" ], "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "docs": "documentation readme src/index.ts -s API", + "build": "tsc", + "test": "jest" }, "repository": { "type": "git", @@ -16,9 +18,6 @@ "keywords": [ "codeceptjs" ], - "scripts": { - "docs": "documentation readme helper.js -s API" - }, "author": "Michael Bodnarchuk @davert", "license": "ISC", "bugs": { @@ -26,6 +25,13 @@ }, "homepage": "https://github.com/codeceptjs/helper#readme", "devDependencies": { - "documentation": "^13.0.2" + "@types/jest": "^28.1.8", + "documentation": "^13.0.2", + "expect": "^28.1.3", + "jest": "26.6.3", + "ts-jest": "^26.5.6" + }, + "dependencies": { + "typescript": "^5.1.3" } } diff --git a/helper.js b/src/index.ts similarity index 85% rename from helper.js rename to src/index.ts index ad98254..f82d3d2 100644 --- a/helper.js +++ b/src/index.ts @@ -12,11 +12,13 @@ */ class Helper { + private config: any; + private options: any; /** * * @param {*} config */ - constructor(config) { + constructor(config: any) { this.config = config; } @@ -35,7 +37,7 @@ class Helper { * @returns {*} * @protected */ - _validateConfig(config) { + _validateConfig(config: any) { return config; } @@ -44,7 +46,7 @@ class Helper { * @param {*} opts * @protected */ - _setConfig(opts) { + _setConfig(opts: any) { this.options = this._validateConfig(opts); } @@ -82,7 +84,7 @@ class Helper { * @protected */ /* eslint-disable */ - _test(test) { + _test(test: any) { } @@ -92,7 +94,7 @@ class Helper { * @param {Mocha.Test} test * @protected */ - _passed(test) { + _passed(test: any) { } @@ -102,7 +104,7 @@ class Helper { * @param {Mocha.Test} test * @protected */ - _failed(test) { + _failed(test: any) { } @@ -112,7 +114,7 @@ class Helper { * @param {CodeceptJS.Step} step * @protected */ - _beforeStep(step) { + _beforeStep(step: any) { } @@ -122,7 +124,7 @@ class Helper { * @param {CodeceptJS.Step} step * @protected */ - _afterStep(step) { + _afterStep(step: any) { } @@ -132,7 +134,7 @@ class Helper { * @param {Mocha.Suite} suite * @protected */ - _beforeSuite(suite) { + _beforeSuite(suite: any) { } @@ -142,7 +144,7 @@ class Helper { * @param {Mocha.Suite} suite * @protected */ - _afterSuite(suite) { + _afterSuite(suite: any) { } @@ -152,15 +154,16 @@ class Helper { * @param {Mocha.Suite} suite * @protected */ - _finishTest(suite) { + _finishTest(suite: any) { } /** * Abstract method to provide common interface to accessing helpers internals inside a test. */ - _useTo(description, fn) { + _useTo(description: string, fn: any) { if (!description || !fn) throw new Error('useTo requires "description:string" and "fn:async function" as arguments'); + //@ts-ignore if (fn[Symbol.toStringTag] !== 'AsyncFunction') throw new Error(`Not async function!\n${fn}\nNative helpers API is asynchronous, please update this function be async`); fn.toString = () => 'fn()'; return fn(this); @@ -173,6 +176,7 @@ class Helper { * @type {*} */ get helpers() { + // @ts-ignore const { container } = global.codeceptjs || require('codeceptjs'); return container.helpers(); } @@ -182,7 +186,8 @@ class Helper { * * @param {string} msg */ - debug(msg) { + debug(msg: string) { + // @ts-ignore const { output } = global.codeceptjs || require('codeceptjs'); output.debug(msg); } @@ -191,10 +196,11 @@ class Helper { * @param {string} section * @param {string} msg */ - debugSection(section, msg) { + debugSection(section: any, msg: string) { + // @ts-ignore const { output } = global.codeceptjs || require('codeceptjs'); output.debug(`[${section}] ${msg}`); } } -module.exports = Helper; +export default Helper; \ No newline at end of file diff --git a/tests/index.spec.ts b/tests/index.spec.ts new file mode 100644 index 0000000..fc70a9e --- /dev/null +++ b/tests/index.spec.ts @@ -0,0 +1,78 @@ +import Helper from '../src'; + +let _Helper; +let _CustomHelper; + +class CustomHelper extends Helper { + _validateConfig (config: any): any { + super._validateConfig (config); + if (!config.hello) throw Error('your config is not valid!'); + } +} + +describe('Abstract helper', () => { + + beforeAll(() => { + _Helper = new Helper({ hello: 'world'}); + }) + + test('create new helper successfully', () => { + expect(_Helper.constructor.name).toEqual('Helper'); + }); + + test('get the passed config', () => { + expect(_Helper.config).toEqual({ hello: 'world'}); + }); + + test('get the options from passed config', () => { + _Helper._setConfig({ another: 'value' }); + expect(_Helper.config).toEqual({ hello: 'world'}); + expect(_Helper.options).toEqual({ another: 'value'}); + }); + + test('throws error when nothing is passed to _useTo', () => { + try { + _Helper._useTo(); + } catch (e) { + expect(e.message).toContain('useTo requires "description:string" and "fn:async function" as arguments'); + } + }); + + test('throws error when fn is not passed to _useTo', () => { + try { + _Helper._useTo('hello'); + } catch (e) { + expect(e.message).toContain('useTo requires "description:string" and "fn:async function" as arguments'); + } + }); + + test('throws error when description is not passed to _useTo', () => { + try { + _Helper._useTo(undefined, function () {}); + } catch (e) { + expect(e.message).toContain('useTo requires "description:string" and "fn:async function" as arguments'); + } + }); + + test('throws error when non async fn is passed to _useTo', () => { + try { + _Helper._useTo('hello', function () {}); + } catch (e) { + expect(e.message).toContain('Not async function!'); + } + }); + + test('no error when all valid args passed to _useTo', async () => { + const res = _Helper._useTo('hello', async function hello () { return 'hi' }); + expect(await res).toEqual('hi'); + }); + + test('validate config of custom helper', async () => { + try { + _CustomHelper = new CustomHelper({ }); + _CustomHelper._validateConfig(_CustomHelper.config) + } catch (e) { + expect(e.message).toEqual('your config is not valid!'); + } + }); +}) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d3aba34 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "ts-node": { + "files": true + }, + "compilerOptions": { + "target": "es2018", + "lib": ["es2018", "DOM"], + "esModuleInterop": true, + "module": "commonjs", + "strictNullChecks": false, + "types": ["jest", "node"], + "declaration": true, + "outDir": "./dist", + "baseUrl": "./src", + "skipLibCheck": true + }, + "exclude": ["node_modules", + "tests/index.spec.ts" + ] +}