Test and Automate Features behind the Flags using Cypress Tests (#206)
* nodejs cypress test launchdarkly * Delete README.md * refactor code * refactor code * code cleanup Co-authored-by: Arpendu Kumar Garai <Arpendu.KumarGarai@microfocus.com>
This commit is contained in:
committed by
GitHub
parent
cd3e68cc66
commit
3930e1cb03
11
nodejs/react-cypress-launchdarkly-feature-flag-test/.babelrc
Normal file
11
nodejs/react-cypress-launchdarkly-feature-flag-test/.babelrc
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-runtime",
|
||||
"babel-plugin-styled-components"
|
||||
]
|
||||
}
|
||||
2
nodejs/react-cypress-launchdarkly-feature-flag-test/.env
Normal file
2
nodejs/react-cypress-launchdarkly-feature-flag-test/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
LAUNCH_DARKLY_PROJECT_KEY="default"
|
||||
LAUNCH_DARKLY_AUTH_TOKEN="api-********-****-****-****-************"
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"parserOptions": {
|
||||
"allowImportExportEverywhere": true
|
||||
},
|
||||
"extends": [
|
||||
"airbnb",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"plugins": [
|
||||
"babel"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-parens": 0,
|
||||
"eol-last": 0,
|
||||
"global-require": 0,
|
||||
"arrow-body-style": 0,
|
||||
"consistent-return": 0,
|
||||
"no-unneeded-ternary": 0,
|
||||
"max-len": 0,
|
||||
"no-param-reassign": 2,
|
||||
"new-cap": 0,
|
||||
"no-console": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"spaced-comment": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"import/first": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-mutable-exports": 0,
|
||||
"import/no-named-as-default": 0,
|
||||
"react/display-name": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/jsx-indent": 0,
|
||||
"react/jsx-indent-props": 0,
|
||||
"react/jsx-space-before-closing": 0,
|
||||
"react/jsx-first-prop-new-line": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/require-extension": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-wrap-multilines": 0,
|
||||
"react/jsx-no-bind": 0,
|
||||
"react/jsx-users-react": 0,
|
||||
"react/jsx-tag-spacing": 0,
|
||||
"jsx-a11y/anchor-is-valid": 0,
|
||||
"jsx-a11y/img-has-alt": 0,
|
||||
"no-trailing-spaces": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-duplicate-imports": 0,
|
||||
"import/no-duplicates": 1,
|
||||
"no-useless-escape": 0,
|
||||
"no-unused-expressions": [1 , {"allowTernary": true}],
|
||||
"react/forbid-prop-types": 0
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"jest": true,
|
||||
"node": true
|
||||
},
|
||||
"globals": {
|
||||
"React": true,
|
||||
"fetch": true,
|
||||
"jest": true
|
||||
}
|
||||
}
|
||||
17
nodejs/react-cypress-launchdarkly-feature-flag-test/.github/workflows/ci.yml
vendored
Normal file
17
nodejs/react-cypress-launchdarkly-feature-flag-test/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: ci
|
||||
on: push
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout 🛎
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run tests 🧪
|
||||
# https://github.com/cypress-io/github-action
|
||||
uses: cypress-io/github-action@v3
|
||||
with:
|
||||
start: 'yarn start'
|
||||
env:
|
||||
LAUNCH_DARKLY_PROJECT_KEY: ${{ secrets.LAUNCH_DARKLY_PROJECT_KEY }}
|
||||
LAUNCH_DARKLY_AUTH_TOKEN: ${{ secrets.LAUNCH_DARKLY_AUTH_TOKEN }}
|
||||
6
nodejs/react-cypress-launchdarkly-feature-flag-test/.gitignore
vendored
Normal file
6
nodejs/react-cypress-launchdarkly-feature-flag-test/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
.idea
|
||||
npm-debug.log
|
||||
dist
|
||||
.eslintcache
|
||||
.as-a.ini
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Related Blog Posts
|
||||
|
||||
* [Automated Tests with Feature Flags and Cypress](https://reflectoring.io/nodejs-feature-flag-launchdarkly-react-cypress/)
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fixturesFolder": false,
|
||||
"supportFile": false,
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"viewportHeight": 300
|
||||
}
|
||||
94
nodejs/react-cypress-launchdarkly-feature-flag-test/cypress/integration/spec.js
vendored
Normal file
94
nodejs/react-cypress-launchdarkly-feature-flag-test/cypress/integration/spec.js
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
before(() => {
|
||||
expect(Cypress.env('launchDarklyApiAvailable'), 'LaunchDarkly').to.be.true
|
||||
});
|
||||
|
||||
const featureFlagKey = 'test-greeting-from-cypress';
|
||||
const userId = 'CYPRESS_TEST_1234';
|
||||
|
||||
it('shows a casual greeting', () => {
|
||||
// target the given user to receive the first variation of the feature flag
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 0,
|
||||
})
|
||||
cy.visit('/')
|
||||
cy.contains('h1', 'Hello, World !!').should('be.visible')
|
||||
});
|
||||
|
||||
it('shows a formal greeting', () => {
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 1,
|
||||
})
|
||||
cy.visit('/')
|
||||
cy.contains('h1', 'Good Morning, World !!').should('be.visible')
|
||||
});
|
||||
|
||||
it('shows a vacation greeting', () => {
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 2,
|
||||
})
|
||||
cy.visit('/')
|
||||
cy.contains('h1', 'Hurrayyyyy, World').should('be.visible')
|
||||
|
||||
// print the current state of the feature flag and its variations
|
||||
cy.task('cypress-ld-control:getFeatureFlag', featureFlagKey)
|
||||
.then(console.log)
|
||||
// let's print the variations to the Command Log side panel
|
||||
.its('variations')
|
||||
.then((variations) => {
|
||||
variations.forEach((v, k) => {
|
||||
cy.log(`${k}: ${v.name} is ${v.value}`)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
it('shows all greetings', () => {
|
||||
cy.visit('/')
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 0,
|
||||
})
|
||||
cy.contains('h1', 'Hello, World !!')
|
||||
.should('be.visible')
|
||||
.wait(1000)
|
||||
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 1,
|
||||
})
|
||||
cy.contains('h1', 'Good Morning, World !!').should('be.visible').wait(1000)
|
||||
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey,
|
||||
userId,
|
||||
variationIndex: 2,
|
||||
})
|
||||
cy.contains('h1', 'Hurrayyyyy, World !!').should('be.visible')
|
||||
});
|
||||
|
||||
it('click a button', () => {
|
||||
cy.task('cypress-ld-control:setFeatureFlagForUser', {
|
||||
featureFlagKey: 'show-shiny-new-feature',
|
||||
userId: 'john_doe',
|
||||
variationIndex: 0,
|
||||
})
|
||||
cy.visit('/');
|
||||
var alerted = false;
|
||||
cy.on('window:alert', msg => alerted = msg);
|
||||
|
||||
cy.get('#shiny-button').should('be.visible').click().then(
|
||||
() => expect(alerted).to.match(/A new shiny feature pops up!/));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.task('cypress-ld-control:removeUserTarget', { featureFlagKey, userId })
|
||||
});
|
||||
31
nodejs/react-cypress-launchdarkly-feature-flag-test/cypress/plugins/index.js
vendored
Normal file
31
nodejs/react-cypress-launchdarkly-feature-flag-test/cypress/plugins/index.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
const { initLaunchDarklyApiTasks } = require('cypress-ld-control');
|
||||
require('dotenv').config();
|
||||
module.exports = (on, config) => {
|
||||
const tasks = {
|
||||
// add your other Cypress tasks if any
|
||||
}
|
||||
|
||||
if (
|
||||
process.env.LAUNCH_DARKLY_PROJECT_KEY &&
|
||||
process.env.LAUNCH_DARKLY_AUTH_TOKEN
|
||||
) {
|
||||
const ldApiTasks = initLaunchDarklyApiTasks({
|
||||
projectKey: process.env.LAUNCH_DARKLY_PROJECT_KEY,
|
||||
authToken: process.env.LAUNCH_DARKLY_AUTH_TOKEN,
|
||||
environment: 'production', // the name of your environment to use
|
||||
})
|
||||
// copy all LaunchDarkly methods as individual tasks
|
||||
Object.assign(tasks, ldApiTasks)
|
||||
// set an environment variable for specs to use
|
||||
// to check if the LaunchDarkly can be controlled
|
||||
config.env.launchDarklyApiAvailable = true
|
||||
} else {
|
||||
console.log('Skipping cypress-ld-control plugin')
|
||||
}
|
||||
|
||||
// register all tasks with Cypress
|
||||
on('task', tasks)
|
||||
|
||||
// IMPORTANT: return the updated config object
|
||||
return config
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "react-cypress-launchdarkly-feature-flag-test",
|
||||
"version": "1.1.0",
|
||||
"description": "Example usage of launchdarkly-react-client-sdk",
|
||||
"main": "src/server/index.js",
|
||||
"scripts": {
|
||||
"start": "node src/server/index.js",
|
||||
"lint": "eslint ./src",
|
||||
"serve": "webpack-serve webpack.config.server",
|
||||
"test": "start-test 3000 'cypress open'"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/launchdarkly/js-client-sdk.git"
|
||||
},
|
||||
"keywords": [
|
||||
"launchdarkly",
|
||||
"launch",
|
||||
"darkly",
|
||||
"react",
|
||||
"sdk",
|
||||
"bindings"
|
||||
],
|
||||
"author": "LaunchDarkly <team@launchdarkly.com>",
|
||||
"license": "Apache-2.0",
|
||||
"homepage": "https://github.com/launchdarkly/js-client-sdk/tree/master/packages/launchdarkly-react-client-sdk",
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.16.4",
|
||||
"launchdarkly-react-client-sdk": "^2.22.1",
|
||||
"lodash": "^4.17.21",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.11.0",
|
||||
"react-dom": "^16.11.0",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"styled-components": "^4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.2.3",
|
||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||
"@babel/preset-env": "^7.2.3",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^8.0.4",
|
||||
"babel-plugin-styled-components": "^1.10.0",
|
||||
"cypress": "^9.5.1",
|
||||
"cypress-ld-control": "^1.1.2",
|
||||
"eslint": "^5.10.0",
|
||||
"eslint-config-airbnb": "^17.1.0",
|
||||
"eslint-config-prettier": "^3.3.0",
|
||||
"eslint-plugin-babel": "^5.3.0",
|
||||
"eslint-plugin-import": "^2.14.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.1.2",
|
||||
"eslint-plugin-react": "^7.11.1",
|
||||
"prettier": "^2.5.1",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"universal-hot-reload": "^3.3.4",
|
||||
"webpack": "^4.27.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"webpack-serve": "^2.0.3"
|
||||
}
|
||||
}
|
||||
11
nodejs/react-cypress-launchdarkly-feature-flag-test/src/client/index.js
vendored
Normal file
11
nodejs/react-cypress-launchdarkly-feature-flag-test/src/client/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import App from '../universal/app';
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>,
|
||||
document.getElementById('reactDiv'),
|
||||
);
|
||||
9
nodejs/react-cypress-launchdarkly-feature-flag-test/src/server/index.js
vendored
Normal file
9
nodejs/react-cypress-launchdarkly-feature-flag-test/src/server/index.js
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
const UniversalHotReload = require('universal-hot-reload').default;
|
||||
|
||||
// supply your own webpack configs
|
||||
const serverConfig = require('../../webpack.config.server.js');
|
||||
const clientConfig = require('../../webpack.config.client.js');
|
||||
|
||||
// the configs are optional, you can supply either one or both.
|
||||
// if you omit say the server config, then your server won't hot reload.
|
||||
UniversalHotReload({ serverConfig, clientConfig });
|
||||
35
nodejs/react-cypress-launchdarkly-feature-flag-test/src/server/server.js
vendored
Normal file
35
nodejs/react-cypress-launchdarkly-feature-flag-test/src/server/server.js
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
import Express from 'express';
|
||||
import React from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { StaticRouter } from 'react-router-dom';
|
||||
import App from '../universal/app';
|
||||
|
||||
const PORT = 3000;
|
||||
const app = Express();
|
||||
|
||||
app.use('/dist', Express.static('dist', { maxAge: '1d' }));
|
||||
|
||||
app.use((req, res) => {
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ld-react example</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="reactDiv">${renderToString(
|
||||
<StaticRouter location={req.url} context={{}}>
|
||||
<App />
|
||||
</StaticRouter>,
|
||||
)}</div>
|
||||
<script type="application/javascript" src="http://localhost:3002/dist/bundle.js"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
res.end(html);
|
||||
});
|
||||
|
||||
export default app.listen(PORT, () => {
|
||||
console.log(`Example app listening at ${PORT}...`);
|
||||
});
|
||||
29
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/app.js
vendored
Normal file
29
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/app.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
import { Switch, Route, Redirect } from 'react-router-dom';
|
||||
import { withLDProvider } from 'launchdarkly-react-client-sdk';
|
||||
import SiteNav from './siteNav';
|
||||
import Home from './home';
|
||||
import HooksDemo from './hooksDemo';
|
||||
|
||||
const App = () => (
|
||||
<div>
|
||||
<SiteNav />
|
||||
<main>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/home">
|
||||
<Redirect to="/" />
|
||||
</Route>
|
||||
<Route path="/hooks" component={HooksDemo} />
|
||||
</Switch>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Set clientSideID to your own Client-side ID. You can find this in
|
||||
// your LaunchDarkly portal under Account settings / Projects
|
||||
// https://docs.launchdarkly.com/sdk/client-side/javascript#initializing-the-client
|
||||
const user = {
|
||||
key: 'CYPRESS_TEST_1234'
|
||||
};
|
||||
export default withLDProvider({ clientSideID: '63**********************', user })(App);
|
||||
64
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/home.js
vendored
Normal file
64
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/home.js
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { withLDConsumer } from 'launchdarkly-react-client-sdk';
|
||||
|
||||
const Root = styled.div`
|
||||
color: #001b44;
|
||||
`;
|
||||
const Heading = styled.h1`
|
||||
color: #00449e;
|
||||
`;
|
||||
const theme = {
|
||||
blue: {
|
||||
default: "#3f51b5",
|
||||
hover: "#283593"
|
||||
}
|
||||
};
|
||||
|
||||
const Button = styled.button`
|
||||
background-color: ${(props) => theme[props.theme].default};
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
border-radius: 5px;
|
||||
outline: 0;
|
||||
text-transform: uppercase;
|
||||
margin: 10px 0px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 2px 2px lightgray;
|
||||
transition: ease background-color 250ms;
|
||||
&:hover {
|
||||
background-color: ${(props) => theme[props.theme].hover};
|
||||
}
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.7;
|
||||
}
|
||||
`;
|
||||
|
||||
const clickMe = () => {
|
||||
alert("A new shiny feature pops up!");
|
||||
};
|
||||
|
||||
const Home = ({ flags }) => (
|
||||
<Root>
|
||||
<Heading>{flags.testGreetingFromCypress}, World !!</Heading>
|
||||
<div>
|
||||
This is a LaunchDarkly React example project. The message above changes the greeting,
|
||||
based on the current feature flag variation.
|
||||
</div>
|
||||
<div>
|
||||
{flags.showShinyNewFeature ?
|
||||
<Button id='shiny-button' theme='blue' onClick={clickMe}>Shiny New Feature</Button>: ''}
|
||||
</div>
|
||||
<div>
|
||||
{flags.showShinyNewFeature ? 'This button will show new shiny feature in UI on clicking it.': ''}
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
|
||||
Home.propTypes = {
|
||||
flags: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withLDConsumer()(Home);
|
||||
63
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/hooksDemo.js
vendored
Normal file
63
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/hooksDemo.js
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useFlags } from 'launchdarkly-react-client-sdk';
|
||||
|
||||
const Root = styled.div`
|
||||
color: #001b44;
|
||||
`;
|
||||
const Heading = styled.h1`
|
||||
color: #00449e;
|
||||
`;
|
||||
const theme = {
|
||||
blue: {
|
||||
default: "#3f51b5",
|
||||
hover: "#283593"
|
||||
}
|
||||
};
|
||||
|
||||
const Button = styled.button`
|
||||
background-color: ${(props) => theme[props.theme].default};
|
||||
color: white;
|
||||
padding: 5px 15px;
|
||||
border-radius: 5px;
|
||||
outline: 0;
|
||||
text-transform: uppercase;
|
||||
margin: 10px 0px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 2px 2px lightgray;
|
||||
transition: ease background-color 250ms;
|
||||
&:hover {
|
||||
background-color: ${(props) => theme[props.theme].hover};
|
||||
}
|
||||
&:disabled {
|
||||
cursor: default;
|
||||
opacity: 0.7;
|
||||
}
|
||||
`;
|
||||
|
||||
const clickMe = () => {
|
||||
alert("A new shiny feature pops up!");
|
||||
};
|
||||
|
||||
const HooksDemo = () => {
|
||||
const { testGreetingFromCypress, showShinyNewFeature } = useFlags();
|
||||
|
||||
return (
|
||||
<Root>
|
||||
<Heading>{testGreetingFromCypress}, World !!</Heading>
|
||||
<div>
|
||||
This is the equivalent LaunchDarkly React example project using hooks. The message above changes the greeting,
|
||||
based on the current feature flag variation.
|
||||
</div>
|
||||
<div>
|
||||
{showShinyNewFeature ?
|
||||
<Button id='shiny-button' theme='blue' onClick={clickMe}>Shiny New Feature</Button>: ''}
|
||||
</div>
|
||||
<div>
|
||||
{showShinyNewFeature ? 'This button will show new shiny feature in UI on clicking it.': ''}
|
||||
</div>
|
||||
</Root>
|
||||
);
|
||||
};
|
||||
|
||||
export default HooksDemo;
|
||||
16
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/siteNav.js
vendored
Normal file
16
nodejs/react-cypress-launchdarkly-feature-flag-test/src/universal/siteNav.js
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import { css } from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export default () => (
|
||||
<div
|
||||
css={css`
|
||||
display: flex;
|
||||
width: 170px;
|
||||
justify-content: space-between;
|
||||
`}
|
||||
>
|
||||
<Link to="/">Home</Link>
|
||||
<Link to="/hooks">Hooks Demo</Link>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,27 @@
|
||||
const path = require('path');
|
||||
|
||||
const WebpackServeUrl = 'http://localhost:3002';
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
devtool: 'source-map',
|
||||
entry: ['@babel/polyfill', './src/client/index'],
|
||||
output: {
|
||||
path: path.resolve('dist'),
|
||||
publicPath: `${WebpackServeUrl}/dist/`, // MUST BE FULL PATH!
|
||||
filename: 'bundle.js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
include: path.resolve('src'),
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
const path = require('path');
|
||||
const nodeExternals = require('webpack-node-externals');
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
devtool: 'source-map',
|
||||
entry: ['@babel/polyfill', './src/server/server.js'], // set this to your server entry point. This should be where you start your express server with .listen()
|
||||
target: 'node', // tell webpack this bundle will be used in nodejs environment.
|
||||
externals: [nodeExternals()], // Omit node_modules code from the bundle. You don't want and don't need them in the bundle.
|
||||
output: {
|
||||
path: path.resolve('dist'),
|
||||
filename: 'serverBundle.js',
|
||||
libraryTarget: 'commonjs2', // IMPORTANT! Add module.exports to the beginning of the bundle, so universal-hot-reload can access your app.
|
||||
},
|
||||
// The rest of the config is pretty standard and can contain other webpack stuff you need.
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.jsx?$/,
|
||||
include: path.resolve('src'),
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user