Skip to content

Commit 3930e1c

Browse files
arpendu11Arpendu Kumar Garai
andauthored
Test and Automate Features behind the Flags using Cypress Tests (thombergs#206)
* nodejs cypress test launchdarkly * Delete README.md * refactor code * refactor code * code cleanup Co-authored-by: Arpendu Kumar Garai <[email protected]>
1 parent cd3e68c commit 3930e1c

File tree

20 files changed

+591
-0
lines changed

20 files changed

+591
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"presets": [
3+
"@babel/preset-env",
4+
"@babel/preset-react"
5+
],
6+
"plugins": [
7+
"@babel/plugin-proposal-class-properties",
8+
"@babel/plugin-transform-runtime",
9+
"babel-plugin-styled-components"
10+
]
11+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
LAUNCH_DARKLY_PROJECT_KEY="default"
2+
LAUNCH_DARKLY_AUTH_TOKEN="api-********-****-****-****-************"
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"parser": "babel-eslint",
3+
"parserOptions": {
4+
"allowImportExportEverywhere": true
5+
},
6+
"extends": [
7+
"airbnb",
8+
"eslint:recommended",
9+
"plugin:react/recommended"
10+
],
11+
"plugins": [
12+
"babel"
13+
],
14+
"rules": {
15+
"arrow-parens": 0,
16+
"eol-last": 0,
17+
"global-require": 0,
18+
"arrow-body-style": 0,
19+
"consistent-return": 0,
20+
"no-unneeded-ternary": 0,
21+
"max-len": 0,
22+
"no-param-reassign": 2,
23+
"new-cap": 0,
24+
"no-console": 0,
25+
"object-curly-spacing": 0,
26+
"spaced-comment": 0,
27+
"import/no-extraneous-dependencies": 0,
28+
"import/first": 0,
29+
"import/prefer-default-export": 0,
30+
"import/no-mutable-exports": 0,
31+
"import/no-named-as-default": 0,
32+
"react/display-name": 0,
33+
"react/jsx-filename-extension": 0,
34+
"react/jsx-indent": 0,
35+
"react/jsx-indent-props": 0,
36+
"react/jsx-space-before-closing": 0,
37+
"react/jsx-first-prop-new-line": 0,
38+
"react/prefer-stateless-function": 0,
39+
"react/jsx-closing-bracket-location": 0,
40+
"react/require-extension": 0,
41+
"react/sort-comp": 0,
42+
"react/jsx-wrap-multilines": 0,
43+
"react/jsx-no-bind": 0,
44+
"react/jsx-users-react": 0,
45+
"react/jsx-tag-spacing": 0,
46+
"jsx-a11y/anchor-is-valid": 0,
47+
"jsx-a11y/img-has-alt": 0,
48+
"no-trailing-spaces": 0,
49+
"no-underscore-dangle": 0,
50+
"no-use-before-define": 0,
51+
"no-duplicate-imports": 0,
52+
"import/no-duplicates": 1,
53+
"no-useless-escape": 0,
54+
"no-unused-expressions": [1 , {"allowTernary": true}],
55+
"react/forbid-prop-types": 0
56+
},
57+
"env": {
58+
"browser": true,
59+
"jest": true,
60+
"node": true
61+
},
62+
"globals": {
63+
"React": true,
64+
"fetch": true,
65+
"jest": true
66+
}
67+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: ci
2+
on: push
3+
jobs:
4+
test:
5+
runs-on: ubuntu-20.04
6+
steps:
7+
- name: Checkout 🛎
8+
uses: actions/checkout@v2
9+
10+
- name: Run tests 🧪
11+
# https://github.com/cypress-io/github-action
12+
uses: cypress-io/github-action@v3
13+
with:
14+
start: 'yarn start'
15+
env:
16+
LAUNCH_DARKLY_PROJECT_KEY: ${{ secrets.LAUNCH_DARKLY_PROJECT_KEY }}
17+
LAUNCH_DARKLY_AUTH_TOKEN: ${{ secrets.LAUNCH_DARKLY_AUTH_TOKEN }}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
.idea
3+
npm-debug.log
4+
dist
5+
.eslintcache
6+
.as-a.ini
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"trailingComma": "all",
3+
"tabWidth": 2,
4+
"semi": false,
5+
"singleQuote": true
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Related Blog Posts
2+
3+
* [Automated Tests with Feature Flags and Cypress](https://reflectoring.io/nodejs-feature-flag-launchdarkly-react-cypress/)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"fixturesFolder": false,
3+
"supportFile": false,
4+
"baseUrl": "http://localhost:3000",
5+
"viewportHeight": 300
6+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/// <reference types="cypress" />
2+
3+
before(() => {
4+
expect(Cypress.env('launchDarklyApiAvailable'), 'LaunchDarkly').to.be.true
5+
});
6+
7+
const featureFlagKey = 'test-greeting-from-cypress';
8+
const userId = 'CYPRESS_TEST_1234';
9+
10+
it('shows a casual greeting', () => {
11+
// target the given user to receive the first variation of the feature flag
12+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
13+
featureFlagKey,
14+
userId,
15+
variationIndex: 0,
16+
})
17+
cy.visit('/')
18+
cy.contains('h1', 'Hello, World !!').should('be.visible')
19+
});
20+
21+
it('shows a formal greeting', () => {
22+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
23+
featureFlagKey,
24+
userId,
25+
variationIndex: 1,
26+
})
27+
cy.visit('/')
28+
cy.contains('h1', 'Good Morning, World !!').should('be.visible')
29+
});
30+
31+
it('shows a vacation greeting', () => {
32+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
33+
featureFlagKey,
34+
userId,
35+
variationIndex: 2,
36+
})
37+
cy.visit('/')
38+
cy.contains('h1', 'Hurrayyyyy, World').should('be.visible')
39+
40+
// print the current state of the feature flag and its variations
41+
cy.task('cypress-ld-control:getFeatureFlag', featureFlagKey)
42+
.then(console.log)
43+
// let's print the variations to the Command Log side panel
44+
.its('variations')
45+
.then((variations) => {
46+
variations.forEach((v, k) => {
47+
cy.log(`${k}: ${v.name} is ${v.value}`)
48+
})
49+
})
50+
});
51+
52+
it('shows all greetings', () => {
53+
cy.visit('/')
54+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
55+
featureFlagKey,
56+
userId,
57+
variationIndex: 0,
58+
})
59+
cy.contains('h1', 'Hello, World !!')
60+
.should('be.visible')
61+
.wait(1000)
62+
63+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
64+
featureFlagKey,
65+
userId,
66+
variationIndex: 1,
67+
})
68+
cy.contains('h1', 'Good Morning, World !!').should('be.visible').wait(1000)
69+
70+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
71+
featureFlagKey,
72+
userId,
73+
variationIndex: 2,
74+
})
75+
cy.contains('h1', 'Hurrayyyyy, World !!').should('be.visible')
76+
});
77+
78+
it('click a button', () => {
79+
cy.task('cypress-ld-control:setFeatureFlagForUser', {
80+
featureFlagKey: 'show-shiny-new-feature',
81+
userId: 'john_doe',
82+
variationIndex: 0,
83+
})
84+
cy.visit('/');
85+
var alerted = false;
86+
cy.on('window:alert', msg => alerted = msg);
87+
88+
cy.get('#shiny-button').should('be.visible').click().then(
89+
() => expect(alerted).to.match(/A new shiny feature pops up!/));
90+
});
91+
92+
after(() => {
93+
cy.task('cypress-ld-control:removeUserTarget', { featureFlagKey, userId })
94+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const { initLaunchDarklyApiTasks } = require('cypress-ld-control');
2+
require('dotenv').config();
3+
module.exports = (on, config) => {
4+
const tasks = {
5+
// add your other Cypress tasks if any
6+
}
7+
8+
if (
9+
process.env.LAUNCH_DARKLY_PROJECT_KEY &&
10+
process.env.LAUNCH_DARKLY_AUTH_TOKEN
11+
) {
12+
const ldApiTasks = initLaunchDarklyApiTasks({
13+
projectKey: process.env.LAUNCH_DARKLY_PROJECT_KEY,
14+
authToken: process.env.LAUNCH_DARKLY_AUTH_TOKEN,
15+
environment: 'production', // the name of your environment to use
16+
})
17+
// copy all LaunchDarkly methods as individual tasks
18+
Object.assign(tasks, ldApiTasks)
19+
// set an environment variable for specs to use
20+
// to check if the LaunchDarkly can be controlled
21+
config.env.launchDarklyApiAvailable = true
22+
} else {
23+
console.log('Skipping cypress-ld-control plugin')
24+
}
25+
26+
// register all tasks with Cypress
27+
on('task', tasks)
28+
29+
// IMPORTANT: return the updated config object
30+
return config
31+
}

0 commit comments

Comments
 (0)