From a837edaa5b4a12fa12d349f3ff292bcf7013cba5 Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Mon, 6 Mar 2023 21:45:59 -0500 Subject: [PATCH] feat: warn the user if the grep tags were not found (#35) * refactor and add a method to get the list of tags * check grep tags to confirm they were found --- .github/workflows/ci.yml | 19 +++++++++++++++ cypress/e2e/unit.js | 33 ++++++++++++++++++++++---- jsconfig.json | 4 +++- src/plugin.js | 36 ++++++++++++++++++++++++----- src/utils.js | 23 ++++++++++++++++++ tests/required-tags/expect-all.json | 8 +++++++ 6 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 tests/required-tags/expect-all.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c312c07..1f103da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,25 @@ jobs: --env grepTags=@special \ --expect-exactly ./tests/required-tags/expect-with-required-tag.json + test-tag-not-found: + runs-on: ubuntu-20.04 + steps: + - name: Checkout ๐Ÿ›Ž + uses: actions/checkout@v3 + + - name: Install Cypress ๐Ÿงช + uses: cypress-io/github-action@v5 + with: + runTests: false + + - name: looking for tag that does not exist + # should show a warning and run all specs + run: | + npx cypress-expect run \ + --project tests/required-tags \ + --env grepTags=@wrong-tag,grepFilterSpecs=true \ + --expect-exactly ./tests/required-tags/expect-all.json + test-required-tags-only: runs-on: ubuntu-20.04 steps: diff --git a/cypress/e2e/unit.js b/cypress/e2e/unit.js index 10fb503..495c9ce 100644 --- a/cypress/e2e/unit.js +++ b/cypress/e2e/unit.js @@ -10,9 +10,32 @@ import { shouldTestRunTags, shouldTestRunRequiredTags, shouldTestRunTitle, + getMentionedTags, } from '../../src/utils' describe('utils', () => { + context('getMentionedTags', () => { + it('returns unique tags', () => { + const tags = getMentionedTags('@tag1+@tag2+@tag3') + expect(tags).to.deep.equal(['@tag1', '@tag2', '@tag3']) + }) + + it('sorts returned tags', () => { + const tags = getMentionedTags('x y a') + expect(tags).to.deep.equal(['a', 'x', 'y']) + }) + + it('handles -', () => { + const tags = getMentionedTags('@smoke+@screen-b') + expect(tags).to.deep.equal(['@screen-b', '@smoke']) + }) + + it('handles extra spaces', () => { + const tags = getMentionedTags(' @tag1 -@tag2 ') + expect(tags).to.deep.equal(['@tag1', '@tag2']) + }) + }) + context('parseTitleGrep', () => { it('grabs the positive title', () => { const parsed = parseTitleGrep('hello w') @@ -51,7 +74,7 @@ describe('utils', () => { }) it('returns null for undefined input', () => { - const parsed = parseTitleGrep() + const parsed = parseTitleGrep(undefined) expect(parsed).to.equal(null) }) @@ -271,10 +294,12 @@ describe('utils', () => { expect(shouldTestRun(parsed, 'hello w'), 'needs tags').to.equal(false) expect(shouldTestRun(parsed, 'hello no')).to.equal(false) // not every tag is present - expect(shouldTestRun(parsed, ['@tag1', '@tag2'])).to.equal(false) - expect(shouldTestRun(parsed, ['@tag1', '@tag2', '@tag3'])).to.equal(true) + expect(shouldTestRun(parsed, '', ['@tag1', '@tag2'])).to.equal(false) + expect(shouldTestRun(parsed, '', ['@tag1', '@tag2', '@tag3'])).to.equal( + true, + ) expect( - shouldTestRun(parsed, ['@tag1', '@tag2', '@tag3', '@tag4']), + shouldTestRun(parsed, '', ['@tag1', '@tag2', '@tag3', '@tag4']), ).to.equal(true) // title matches, but tags do not diff --git a/jsconfig.json b/jsconfig.json index 6d6ef36..462cd47 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,6 +1,8 @@ { "compilerOptions": { "types": ["cypress"], - "resolveJsonModule": true + "resolveJsonModule": true, + "moduleResolution": "node", + "target": "ES6" } } diff --git a/src/plugin.js b/src/plugin.js index 85306c1..345c179 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -6,7 +6,7 @@ const { getTestNames, findEffectiveTestTags } = require('find-test-names') const fs = require('fs') const path = require('path') const { version } = require('../package.json') -const { parseGrep, shouldTestRun } = require('./utils') +const { parseGrep, shouldTestRun, getMentionedTags } = require('./utils') const isCypressV9 = (config) => !('specPattern' in config) @@ -106,8 +106,12 @@ function cypressGrepPlugin(config) { debug('%o', greppedSpecs) } else if (grepTags) { const parsedGrep = parseGrep(null, grepTags) - debug('parsed grep tags %o', parsedGrep) + const mentionedTags = getMentionedTags(grepTags) + debug('user mentioned tags %o', mentionedTags) + // unique tags found across all specs we search + const foundTags = new Set() + greppedSpecs = specFiles.filter((specFile) => { const text = fs.readFileSync(specFile, { encoding: 'utf8' }) @@ -120,6 +124,15 @@ function cypressGrepPlugin(config) { return Object.keys(testTags).some((testTitle) => { const effectiveTags = testTags[testTitle].effectiveTags const requiredTags = testTags[testTitle].requiredTags + + // remember all found tags + effectiveTags.forEach((tag) => { + foundTags.add(tag) + }) + requiredTags.forEach((tag) => { + foundTags.add(tag) + }) + return shouldTestRun( parsedGrep, undefined, @@ -138,6 +151,17 @@ function cypressGrepPlugin(config) { debug('found grep tags "%s" in %d specs', grepTags, greppedSpecs.length) debug('%o', greppedSpecs) + + debug('all found tags across the specs %o', ...foundTags) + debug('user mentioned tags %o', mentionedTags) + mentionedTags.forEach((tag) => { + if (!foundTags.has(tag)) { + console.warn( + 'cy-grep: could not find the tag "%s" in any of the specs', + tag, + ) + } + }) } else { // we have no tags to grep debug('will try eliminating specs with required tags') @@ -190,10 +214,10 @@ function cypressGrepPlugin(config) { } } else { // hmm, we filtered out all specs, probably something is wrong - console.warn('grep and/or grepTags has eliminated all specs') - grep ? console.warn('grep: %s', grep) : null - grepTags ? console.warn('grepTags: %s', grepTags) : null - console.warn('Will leave all specs to run to filter at run-time') + console.warn('cy-grep: grep and/or grepTags has eliminated all specs') + grep ? console.warn('cy-grep: title: %s', grep) : null + grepTags ? console.warn('cy-grep: tags: %s', grepTags) : null + console.warn('cy-grep: Will leave all specs to run to filter at run-time') } } diff --git a/src/utils.js b/src/utils.js index 121c523..e02d82f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -95,6 +95,28 @@ function parseTagsGrep(s) { return ORS_filtered } +/** + * Given a user string of tags to find, with various connectors, + * returns the list of just the tags themselves. Could be used to + * quickly filter test specs or find misspelled tags. + * @returns {string[]} list of unique tags + */ +function getMentionedTags(s) { + if (!s) { + return [] + } + const spaced = s.replaceAll(/[+,]/g, ' ') + const tags = spaced + .split(' ') + .map((s) => s.trim()) + .filter(Boolean) + // remove any "-" at the start of the tag + // because these are to signal inverted tags + .map((s) => (s.startsWith('-') ? s.slice(1) : s)) + const uniqueTags = [...new Set(tags)] + return uniqueTags.sort() +} + function shouldTestRunRequiredTags(parsedGrepTags, requiredTags = []) { if (!requiredTags.length) { // there are no tags to check @@ -216,4 +238,5 @@ module.exports = { shouldTestRunTags, shouldTestRunRequiredTags, shouldTestRunTitle, + getMentionedTags, } diff --git a/tests/required-tags/expect-all.json b/tests/required-tags/expect-all.json new file mode 100644 index 0000000..213de1f --- /dev/null +++ b/tests/required-tags/expect-all.json @@ -0,0 +1,8 @@ +{ + "e2e": { + "spec.cy.js": { + "runs always": "pending", + "runs only when tag \"special\" is on": "pending" + } + } +}