Skip to content

Commit 8b9ddf3

Browse files
committedSep 28, 2019
add example browserstack-local + integration tests in pipelines
1 parent 3b0220a commit 8b9ddf3

35 files changed

+12415
-1
lines changed
 

‎.github/workflows/nodejs.yml

+27-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ jobs:
55
runs-on: ubuntu-latest
66
strategy:
77
matrix:
8-
node-version: [10.x, 12.x]
8+
node-version: [8.x, 10.x, 12.x]
99
steps:
1010
- uses: actions/checkout@v1
1111
- name: Use Node.js ${{ matrix.node-version }}
@@ -21,3 +21,29 @@ jobs:
2121
npm run lint
2222
npm run build
2323
npm run test
24+
integratiom:
25+
needs: build
26+
runs-on: ubuntu-latest
27+
strategy:
28+
matrix:
29+
node-version: [8.x, 10.x, 12.x]
30+
steps:
31+
- uses: actions/checkout@v1
32+
- name: Use Node.js ${{ matrix.node-version }}
33+
uses: actions/setup-node@v1
34+
with:
35+
node-version: ${{ matrix.node-version }}
36+
- name: install and build environment
37+
run: |
38+
npm install
39+
npm run build
40+
- name: run example with Browserstack local
41+
env:
42+
BROWSERSTACK_USER_NAME: ${{ secrets.BROWSERSTACK_USER_NAME }}
43+
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
44+
run: |
45+
cd examples/with-bt-local
46+
yarn
47+
yarn test
48+
49+

‎.gitignore

+20
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,22 @@
11
lib/
2+
*.log
23
node_modules/
4+
/.pnp
5+
.pnp.js
6+
7+
# testing
8+
/coverage
9+
10+
# production
11+
/build
12+
13+
# misc
14+
.DS_Store
15+
.env.local
16+
.env.development.local
17+
.env.test.local
18+
.env.production.local
19+
20+
npm-debug.log*
21+
yarn-debug.log*
22+
yarn-error.log*

‎.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
src/
22
.github/
3+
examples/

‎README.md

+14
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,20 @@ describe('my visual test', () => {
102102

103103
If you aren't willing to put your credentials in your `package.json` file, you can export in your environment `BROWSERSTACK_USER_NAME` and `BROWSERSTACK_ACCESS_KEY`. If you do so, `userName` and `accessKey` can be omitted.
104104

105+
# Examples
106+
107+
In the `examples` folder, you can find an example using `react-create-app`.
108+
109+
To run the test, type the following commands in your terminal:
110+
111+
```bash
112+
cd examples/with-bt-local
113+
yarn install
114+
yarn test
115+
```
116+
117+
The `test` script will run a basic e2e tests, a visual tests making a snapshot of the web-app and the unit-tests.
118+
105119
# Known limitations
106120

107121
For now, only one browser can be defined.

‎examples/with-bt-local/.gitignore

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

‎examples/with-bt-local/README.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2+
3+
## Available Scripts
4+
5+
In the project directory, you can run:
6+
7+
### `npm start`
8+
9+
Runs the app in the development mode.<br>
10+
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11+
12+
The page will reload if you make edits.<br>
13+
You will also see any lint errors in the console.
14+
15+
### `npm test`
16+
17+
Launches the test runner in the interactive watch mode.<br>
18+
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
19+
20+
### `npm run build`
21+
22+
Builds the app for production to the `build` folder.<br>
23+
It correctly bundles React in production mode and optimizes the build for the best performance.
24+
25+
The build is minified and the filenames include the hashes.<br>
26+
Your app is ready to be deployed!
27+
28+
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
29+
30+
### `npm run eject`
31+
32+
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
33+
34+
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
35+
36+
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
37+
38+
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
39+
40+
## Learn More
41+
42+
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
43+
44+
To learn React, check out the [React documentation](https://reactjs.org/).
45+
46+
### Code Splitting
47+
48+
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
49+
50+
### Analyzing the Bundle Size
51+
52+
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
53+
54+
### Making a Progressive Web App
55+
56+
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
57+
58+
### Advanced Configuration
59+
60+
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
61+
62+
### Deployment
63+
64+
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65+
66+
### `npm run build` fails to minify
67+
68+
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

‎examples/with-bt-local/config/env.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const paths = require('./paths');
6+
7+
// Make sure that including paths.js after env.js will read .env variables.
8+
delete require.cache[require.resolve('./paths')];
9+
10+
const NODE_ENV = process.env.NODE_ENV;
11+
if (!NODE_ENV) {
12+
throw new Error(
13+
'The NODE_ENV environment variable is required but was not specified.'
14+
);
15+
}
16+
17+
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18+
const dotenvFiles = [
19+
`${paths.dotenv}.${NODE_ENV}.local`,
20+
`${paths.dotenv}.${NODE_ENV}`,
21+
// Don't include `.env.local` for `test` environment
22+
// since normally you expect tests to produce the same
23+
// results for everyone
24+
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
25+
paths.dotenv,
26+
].filter(Boolean);
27+
28+
// Load environment variables from .env* files. Suppress warnings using silent
29+
// if this file is missing. dotenv will never modify any environment variables
30+
// that have already been set. Variable expansion is supported in .env files.
31+
// https://github.com/motdotla/dotenv
32+
// https://github.com/motdotla/dotenv-expand
33+
dotenvFiles.forEach(dotenvFile => {
34+
if (fs.existsSync(dotenvFile)) {
35+
require('dotenv-expand')(
36+
require('dotenv').config({
37+
path: dotenvFile,
38+
})
39+
);
40+
}
41+
});
42+
43+
// We support resolving modules according to `NODE_PATH`.
44+
// This lets you use absolute paths in imports inside large monorepos:
45+
// https://github.com/facebook/create-react-app/issues/253.
46+
// It works similar to `NODE_PATH` in Node itself:
47+
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48+
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49+
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
50+
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
51+
// We also resolve them to make sure all tools using them work consistently.
52+
const appDirectory = fs.realpathSync(process.cwd());
53+
process.env.NODE_PATH = (process.env.NODE_PATH || '')
54+
.split(path.delimiter)
55+
.filter(folder => folder && !path.isAbsolute(folder))
56+
.map(folder => path.resolve(appDirectory, folder))
57+
.join(path.delimiter);
58+
59+
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60+
// injected into the application via DefinePlugin in Webpack configuration.
61+
const REACT_APP = /^REACT_APP_/i;
62+
63+
function getClientEnvironment(publicUrl) {
64+
const raw = Object.keys(process.env)
65+
.filter(key => REACT_APP.test(key))
66+
.reduce(
67+
(env, key) => {
68+
env[key] = process.env[key];
69+
return env;
70+
},
71+
{
72+
// Useful for determining whether we’re running in production mode.
73+
// Most importantly, it switches React into the correct mode.
74+
NODE_ENV: process.env.NODE_ENV || 'development',
75+
// Useful for resolving the correct path to static assets in `public`.
76+
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
77+
// This should only be used as an escape hatch. Normally you would put
78+
// images into the `src` and `import` them in code to get their paths.
79+
PUBLIC_URL: publicUrl,
80+
}
81+
);
82+
// Stringify all values so we can feed into Webpack DefinePlugin
83+
const stringified = {
84+
'process.env': Object.keys(raw).reduce((env, key) => {
85+
env[key] = JSON.stringify(raw[key]);
86+
return env;
87+
}, {}),
88+
};
89+
90+
return { raw, stringified };
91+
}
92+
93+
module.exports = getClientEnvironment;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
'use strict';
2+
3+
// This is a custom Jest transformer turning style imports into empty objects.
4+
// http://facebook.github.io/jest/docs/en/webpack.html
5+
6+
module.exports = {
7+
process() {
8+
return 'module.exports = {};';
9+
},
10+
getCacheKey() {
11+
// The output is always the same.
12+
return 'cssTransform';
13+
},
14+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
const path = require('path');
4+
const camelcase = require('camelcase');
5+
6+
// This is a custom Jest transformer turning file imports into filenames.
7+
// http://facebook.github.io/jest/docs/en/webpack.html
8+
9+
module.exports = {
10+
process(src, filename) {
11+
const assetFilename = JSON.stringify(path.basename(filename));
12+
13+
if (filename.match(/\.svg$/)) {
14+
// Based on how SVGR generates a component name:
15+
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
16+
const pascalCaseFileName = camelcase(path.parse(filename).name, {
17+
pascalCase: true,
18+
});
19+
const componentName = `Svg${pascalCaseFileName}`;
20+
return `const React = require('react');
21+
module.exports = {
22+
__esModule: true,
23+
default: ${assetFilename},
24+
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
25+
return {
26+
$$typeof: Symbol.for('react.element'),
27+
type: 'svg',
28+
ref: ref,
29+
key: null,
30+
props: Object.assign({}, props, {
31+
children: ${assetFilename}
32+
})
33+
};
34+
}),
35+
};`;
36+
}
37+
38+
return `module.exports = ${assetFilename};`;
39+
},
40+
};
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
'use strict';
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const paths = require('./paths');
6+
const chalk = require('react-dev-utils/chalk');
7+
const resolve = require('resolve');
8+
9+
/**
10+
* Get the baseUrl of a compilerOptions object.
11+
*
12+
* @param {Object} options
13+
*/
14+
function getAdditionalModulePaths(options = {}) {
15+
const baseUrl = options.baseUrl;
16+
17+
// We need to explicitly check for null and undefined (and not a falsy value) because
18+
// TypeScript treats an empty string as `.`.
19+
if (baseUrl == null) {
20+
// If there's no baseUrl set we respect NODE_PATH
21+
// Note that NODE_PATH is deprecated and will be removed
22+
// in the next major release of create-react-app.
23+
24+
const nodePath = process.env.NODE_PATH || '';
25+
return nodePath.split(path.delimiter).filter(Boolean);
26+
}
27+
28+
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
29+
30+
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
31+
// the default behavior.
32+
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
33+
return null;
34+
}
35+
36+
// Allow the user set the `baseUrl` to `appSrc`.
37+
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
38+
return [paths.appSrc];
39+
}
40+
41+
// Otherwise, throw an error.
42+
throw new Error(
43+
chalk.red.bold(
44+
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
45+
' Create React App does not support other values at this time.'
46+
)
47+
);
48+
}
49+
50+
function getModules() {
51+
// Check if TypeScript is setup
52+
const hasTsConfig = fs.existsSync(paths.appTsConfig);
53+
const hasJsConfig = fs.existsSync(paths.appJsConfig);
54+
55+
if (hasTsConfig && hasJsConfig) {
56+
throw new Error(
57+
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
58+
);
59+
}
60+
61+
let config;
62+
63+
// If there's a tsconfig.json we assume it's a
64+
// TypeScript project and set up the config
65+
// based on tsconfig.json
66+
if (hasTsConfig) {
67+
const ts = require(resolve.sync('typescript', {
68+
basedir: paths.appNodeModules,
69+
}));
70+
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
71+
// Otherwise we'll check if there is jsconfig.json
72+
// for non TS projects.
73+
} else if (hasJsConfig) {
74+
config = require(paths.appJsConfig);
75+
}
76+
77+
config = config || {};
78+
const options = config.compilerOptions || {};
79+
80+
const additionalModulePaths = getAdditionalModulePaths(options);
81+
82+
return {
83+
additionalModulePaths: additionalModulePaths,
84+
hasTsConfig,
85+
};
86+
}
87+
88+
module.exports = getModules();
+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
const path = require('path');
4+
const fs = require('fs');
5+
const url = require('url');
6+
7+
// Make sure any symlinks in the project folder are resolved:
8+
// https://github.com/facebook/create-react-app/issues/637
9+
const appDirectory = fs.realpathSync(process.cwd());
10+
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11+
12+
const envPublicUrl = process.env.PUBLIC_URL;
13+
14+
function ensureSlash(inputPath, needsSlash) {
15+
const hasSlash = inputPath.endsWith('/');
16+
if (hasSlash && !needsSlash) {
17+
return inputPath.substr(0, inputPath.length - 1);
18+
} else if (!hasSlash && needsSlash) {
19+
return `${inputPath}/`;
20+
} else {
21+
return inputPath;
22+
}
23+
}
24+
25+
const getPublicUrl = appPackageJson =>
26+
envPublicUrl || require(appPackageJson).homepage;
27+
28+
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
29+
// "public path" at which the app is served.
30+
// Webpack needs to know it to put the right <script> hrefs into HTML even in
31+
// single-page apps that may serve index.html for nested URLs like /todos/42.
32+
// We can't use a relative path in HTML because we don't want to load something
33+
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
34+
function getServedPath(appPackageJson) {
35+
const publicUrl = getPublicUrl(appPackageJson);
36+
const servedUrl =
37+
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
38+
return ensureSlash(servedUrl, true);
39+
}
40+
41+
const moduleFileExtensions = [
42+
'web.mjs',
43+
'mjs',
44+
'web.js',
45+
'js',
46+
'web.ts',
47+
'ts',
48+
'web.tsx',
49+
'tsx',
50+
'json',
51+
'web.jsx',
52+
'jsx',
53+
];
54+
55+
// Resolve file paths in the same order as webpack
56+
const resolveModule = (resolveFn, filePath) => {
57+
const extension = moduleFileExtensions.find(extension =>
58+
fs.existsSync(resolveFn(`${filePath}.${extension}`))
59+
);
60+
61+
if (extension) {
62+
return resolveFn(`${filePath}.${extension}`);
63+
}
64+
65+
return resolveFn(`${filePath}.js`);
66+
};
67+
68+
// config after eject: we're in ./config/
69+
module.exports = {
70+
dotenv: resolveApp('.env'),
71+
appPath: resolveApp('.'),
72+
appBuild: resolveApp('build'),
73+
appPublic: resolveApp('public'),
74+
appHtml: resolveApp('public/index.html'),
75+
appIndexJs: resolveModule(resolveApp, 'src/index'),
76+
appPackageJson: resolveApp('package.json'),
77+
appSrc: resolveApp('src'),
78+
appTsConfig: resolveApp('tsconfig.json'),
79+
appJsConfig: resolveApp('jsconfig.json'),
80+
yarnLockFile: resolveApp('yarn.lock'),
81+
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
82+
proxySetup: resolveApp('src/setupProxy.js'),
83+
appNodeModules: resolveApp('node_modules'),
84+
publicUrl: getPublicUrl(resolveApp('package.json')),
85+
servedPath: getServedPath(resolveApp('package.json')),
86+
};
87+
88+
89+
90+
module.exports.moduleFileExtensions = moduleFileExtensions;
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
3+
const { resolveModuleName } = require('ts-pnp');
4+
5+
exports.resolveModuleName = (
6+
typescript,
7+
moduleName,
8+
containingFile,
9+
compilerOptions,
10+
resolutionHost
11+
) => {
12+
return resolveModuleName(
13+
moduleName,
14+
containingFile,
15+
compilerOptions,
16+
resolutionHost,
17+
typescript.resolveModuleName
18+
);
19+
};
20+
21+
exports.resolveTypeReferenceDirective = (
22+
typescript,
23+
moduleName,
24+
containingFile,
25+
compilerOptions,
26+
resolutionHost
27+
) => {
28+
return resolveModuleName(
29+
moduleName,
30+
containingFile,
31+
compilerOptions,
32+
resolutionHost,
33+
typescript.resolveTypeReferenceDirective
34+
);
35+
};

‎examples/with-bt-local/config/webpack.config.js

+656
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
'use strict';
2+
3+
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
4+
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
5+
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
6+
const ignoredFiles = require('react-dev-utils/ignoredFiles');
7+
const paths = require('./paths');
8+
const fs = require('fs');
9+
10+
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
11+
const host = process.env.HOST || '0.0.0.0';
12+
13+
module.exports = function(proxy, allowedHost) {
14+
return {
15+
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
16+
// websites from potentially accessing local content through DNS rebinding:
17+
// https://github.com/webpack/webpack-dev-server/issues/887
18+
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
19+
// However, it made several existing use cases such as development in cloud
20+
// environment or subdomains in development significantly more complicated:
21+
// https://github.com/facebook/create-react-app/issues/2271
22+
// https://github.com/facebook/create-react-app/issues/2233
23+
// While we're investigating better solutions, for now we will take a
24+
// compromise. Since our WDS configuration only serves files in the `public`
25+
// folder we won't consider accessing them a vulnerability. However, if you
26+
// use the `proxy` feature, it gets more dangerous because it can expose
27+
// remote code execution vulnerabilities in backends like Django and Rails.
28+
// So we will disable the host check normally, but enable it if you have
29+
// specified the `proxy` setting. Finally, we let you override it if you
30+
// really know what you're doing with a special environment variable.
31+
disableHostCheck:
32+
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
33+
// Enable gzip compression of generated files.
34+
compress: true,
35+
// Silence WebpackDevServer's own logs since they're generally not useful.
36+
// It will still show compile warnings and errors with this setting.
37+
clientLogLevel: 'none',
38+
// By default WebpackDevServer serves physical files from current directory
39+
// in addition to all the virtual build products that it serves from memory.
40+
// This is confusing because those files won’t automatically be available in
41+
// production build folder unless we copy them. However, copying the whole
42+
// project directory is dangerous because we may expose sensitive files.
43+
// Instead, we establish a convention that only files in `public` directory
44+
// get served. Our build script will copy `public` into the `build` folder.
45+
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
46+
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
47+
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
48+
// Note that we only recommend to use `public` folder as an escape hatch
49+
// for files like `favicon.ico`, `manifest.json`, and libraries that are
50+
// for some reason broken when imported through Webpack. If you just want to
51+
// use an image, put it in `src` and `import` it from JavaScript instead.
52+
contentBase: paths.appPublic,
53+
// By default files from `contentBase` will not trigger a page reload.
54+
watchContentBase: true,
55+
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
56+
// for the WebpackDevServer client so it can learn when the files were
57+
// updated. The WebpackDevServer client is included as an entry point
58+
// in the Webpack development configuration. Note that only changes
59+
// to CSS are currently hot reloaded. JS changes will refresh the browser.
60+
hot: true,
61+
// It is important to tell WebpackDevServer to use the same "root" path
62+
// as we specified in the config. In development, we always serve from /.
63+
publicPath: '/',
64+
// WebpackDevServer is noisy by default so we emit custom message instead
65+
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
66+
quiet: true,
67+
// Reportedly, this avoids CPU overload on some systems.
68+
// https://github.com/facebook/create-react-app/issues/293
69+
// src/node_modules is not ignored to support absolute imports
70+
// https://github.com/facebook/create-react-app/issues/1065
71+
watchOptions: {
72+
ignored: ignoredFiles(paths.appSrc),
73+
},
74+
// Enable HTTPS if the HTTPS environment variable is set to 'true'
75+
https: protocol === 'https',
76+
host,
77+
overlay: false,
78+
historyApiFallback: {
79+
// Paths with dots should still use the history fallback.
80+
// See https://github.com/facebook/create-react-app/issues/387.
81+
disableDotRule: true,
82+
},
83+
public: allowedHost,
84+
proxy,
85+
before(app, server) {
86+
if (fs.existsSync(paths.proxySetup)) {
87+
// This registers user provided middleware for proxy reasons
88+
require(paths.proxySetup)(app);
89+
}
90+
91+
// This lets us fetch source contents from webpack for the error overlay
92+
app.use(evalSourceMapMiddleware(server));
93+
// This lets us open files from the runtime error overlay.
94+
app.use(errorOverlayMiddleware());
95+
96+
// This service worker file is effectively a 'no-op' that will reset any
97+
// previous service worker registered for the same host:port combination.
98+
// We do this in development to avoid hitting the production cache if
99+
// it used the same host and port.
100+
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
101+
app.use(noopServiceWorkerMiddleware());
102+
},
103+
};
104+
};

‎examples/with-bt-local/package.json

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
{
2+
"name": "with-bt-local",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@babel/core": "7.6.0",
7+
"@svgr/webpack": "4.3.2",
8+
"@typescript-eslint/eslint-plugin": "^2.2.0",
9+
"@typescript-eslint/parser": "^2.2.0",
10+
"babel-eslint": "10.0.3",
11+
"babel-jest": "^24.9.0",
12+
"babel-loader": "8.0.6",
13+
"babel-plugin-named-asset-import": "^0.3.4",
14+
"babel-preset-react-app": "^9.0.2",
15+
"camelcase": "^5.2.0",
16+
"case-sensitive-paths-webpack-plugin": "2.2.0",
17+
"css-loader": "2.1.1",
18+
"dotenv": "6.2.0",
19+
"dotenv-expand": "5.1.0",
20+
"eslint": "^6.1.0",
21+
"eslint-config-react-app": "^5.0.2",
22+
"eslint-loader": "3.0.0",
23+
"eslint-plugin-flowtype": "3.13.0",
24+
"eslint-plugin-import": "2.18.2",
25+
"eslint-plugin-jsx-a11y": "6.2.3",
26+
"eslint-plugin-react": "7.14.3",
27+
"eslint-plugin-react-hooks": "^1.6.1",
28+
"file-loader": "3.0.1",
29+
"fs-extra": "7.0.1",
30+
"html-webpack-plugin": "4.0.0-beta.5",
31+
"identity-obj-proxy": "3.0.0",
32+
"is-wsl": "^1.1.0",
33+
"jest": "24.9.0",
34+
"jest-environment-browserstack": "file:../../../jest-environment-browserstack/",
35+
"jest-environment-jsdom-fourteen": "0.1.0",
36+
"jest-image-snapshot": "^2.11.0",
37+
"jest-resolve": "24.9.0",
38+
"jest-watch-typeahead": "0.4.0",
39+
"mini-css-extract-plugin": "0.8.0",
40+
"npm-run-all": "^4.1.5",
41+
"optimize-css-assets-webpack-plugin": "5.0.3",
42+
"pnp-webpack-plugin": "1.5.0",
43+
"postcss-flexbugs-fixes": "4.1.0",
44+
"postcss-loader": "3.0.0",
45+
"postcss-normalize": "7.0.1",
46+
"postcss-preset-env": "6.7.0",
47+
"postcss-safe-parser": "4.0.1",
48+
"react": "^16.10.0",
49+
"react-app-polyfill": "^1.0.3",
50+
"react-dev-utils": "^9.0.4",
51+
"react-dom": "^16.10.0",
52+
"resolve": "1.12.0",
53+
"resolve-url-loader": "3.1.0",
54+
"sass-loader": "7.2.0",
55+
"selenium-webdriver": "^4.0.0-alpha.5",
56+
"semver": "6.3.0",
57+
"style-loader": "1.0.0",
58+
"terser-webpack-plugin": "1.4.1",
59+
"ts-pnp": "1.1.4",
60+
"url-loader": "2.1.0",
61+
"webpack": "4.40.2",
62+
"webpack-dev-server": "3.2.1",
63+
"webpack-manifest-plugin": "2.0.4",
64+
"workbox-webpack-plugin": "4.3.1"
65+
},
66+
"scripts": {
67+
"start": "PORT=8080 node scripts/start.js",
68+
"build": "node scripts/build.js",
69+
"test:jest": "jest --ci",
70+
"test": "npm-run-all -p -r start test:jest"
71+
},
72+
"eslintConfig": {
73+
"extends": "react-app"
74+
},
75+
"jest": {
76+
"globals": {
77+
"browserstack": {
78+
"capabilities": {
79+
"browserName": "chrome",
80+
"browserVersion": "76.0",
81+
"bstack:options": {
82+
"os": "Windows",
83+
"osVersion": "10",
84+
"buildName": "with-bt-local",
85+
"local": true
86+
}
87+
},
88+
"localTesting": {
89+
"verbose": true,
90+
"local": true,
91+
"forceLocal": true
92+
}
93+
}
94+
},
95+
"roots": [
96+
"<rootDir>/src"
97+
],
98+
"collectCoverageFrom": [
99+
"src/**/*.{js,jsx,ts,tsx}",
100+
"!src/**/*.d.ts"
101+
],
102+
"setupFiles": [
103+
"react-app-polyfill/jsdom"
104+
],
105+
"setupFilesAfterEnv": [],
106+
"testMatch": [
107+
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
108+
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
109+
],
110+
"testEnvironment": "jest-environment-jsdom-fourteen",
111+
"transform": {
112+
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
113+
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
114+
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
115+
},
116+
"transformIgnorePatterns": [
117+
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
118+
"^.+\\.module\\.(css|sass|scss)$"
119+
],
120+
"modulePaths": [],
121+
"moduleNameMapper": {
122+
"^react-native$": "react-native-web",
123+
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
124+
},
125+
"moduleFileExtensions": [
126+
"web.js",
127+
"js",
128+
"web.ts",
129+
"ts",
130+
"web.tsx",
131+
"tsx",
132+
"json",
133+
"web.jsx",
134+
"jsx",
135+
"node"
136+
],
137+
"watchPlugins": [
138+
"jest-watch-typeahead/filename",
139+
"jest-watch-typeahead/testname"
140+
]
141+
},
142+
"browserslist": {
143+
"production": [
144+
">0.2%",
145+
"not dead",
146+
"not op_mini all"
147+
],
148+
"development": [
149+
"last 1 chrome version",
150+
"last 1 firefox version",
151+
"last 1 safari version"
152+
]
153+
},
154+
"babel": {
155+
"presets": [
156+
"react-app"
157+
]
158+
}
159+
}
21.9 KB
Binary file not shown.
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
<meta name="theme-color" content="#000000" />
8+
<meta
9+
name="description"
10+
content="Web site created using create-react-app"
11+
/>
12+
<link rel="apple-touch-icon" href="logo192.png" />
13+
<!--
14+
manifest.json provides metadata used when your web app is installed on a
15+
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16+
-->
17+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18+
<!--
19+
Notice the use of %PUBLIC_URL% in the tags above.
20+
It will be replaced with the URL of the `public` folder during the build.
21+
Only files inside the `public` folder can be referenced from the HTML.
22+
23+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24+
work correctly both with client-side routing and a non-root public URL.
25+
Learn how to configure a non-root public URL by running `npm run build`.
26+
-->
27+
<title>React App</title>
28+
</head>
29+
<body>
30+
<noscript>You need to enable JavaScript to run this app.</noscript>
31+
<div id="root"></div>
32+
<!--
33+
This HTML file is a template.
34+
If you open it directly in the browser, you will see an empty page.
35+
36+
You can add webfonts, meta tags, or analytics to this file.
37+
The build step will place the bundled scripts into the <body> tag.
38+
39+
To begin the development, run `npm start` or `yarn start`.
40+
To create a production bundle, use `npm run build` or `yarn build`.
41+
-->
42+
</body>
43+
</html>
8.38 KB
Loading
22.4 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"short_name": "React App",
3+
"name": "Create React App Sample",
4+
"icons": [
5+
{
6+
"src": "favicon.ico",
7+
"sizes": "64x64 32x32 24x24 16x16",
8+
"type": "image/x-icon"
9+
},
10+
{
11+
"src": "logo192.png",
12+
"type": "image/png",
13+
"sizes": "192x192"
14+
},
15+
{
16+
"src": "logo512.png",
17+
"type": "image/png",
18+
"sizes": "512x512"
19+
}
20+
],
21+
"start_url": ".",
22+
"display": "standalone",
23+
"theme_color": "#000000",
24+
"background_color": "#ffffff"
25+
}
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# https://www.robotstxt.org/robotstxt.html
2+
User-agent: *
+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
'use strict';
2+
3+
// Do this as the first thing so that any code reading it knows the right env.
4+
process.env.BABEL_ENV = 'production';
5+
process.env.NODE_ENV = 'production';
6+
7+
// Makes the script crash on unhandled rejections instead of silently
8+
// ignoring them. In the future, promise rejections that are not handled will
9+
// terminate the Node.js process with a non-zero exit code.
10+
process.on('unhandledRejection', err => {
11+
throw err;
12+
});
13+
14+
// Ensure environment variables are read.
15+
require('../config/env');
16+
17+
18+
const path = require('path');
19+
const chalk = require('react-dev-utils/chalk');
20+
const fs = require('fs-extra');
21+
const webpack = require('webpack');
22+
const configFactory = require('../config/webpack.config');
23+
const paths = require('../config/paths');
24+
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
25+
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
26+
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
27+
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
28+
const printBuildError = require('react-dev-utils/printBuildError');
29+
30+
const measureFileSizesBeforeBuild =
31+
FileSizeReporter.measureFileSizesBeforeBuild;
32+
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
33+
const useYarn = fs.existsSync(paths.yarnLockFile);
34+
35+
// These sizes are pretty large. We'll warn for bundles exceeding them.
36+
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
37+
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
38+
39+
const isInteractive = process.stdout.isTTY;
40+
41+
// Warn and crash if required files are missing
42+
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
43+
process.exit(1);
44+
}
45+
46+
// Generate configuration
47+
const config = configFactory('production');
48+
49+
// We require that you explicitly set browsers and do not fall back to
50+
// browserslist defaults.
51+
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
52+
checkBrowsers(paths.appPath, isInteractive)
53+
.then(() => {
54+
// First, read the current file sizes in build directory.
55+
// This lets us display how much they changed later.
56+
return measureFileSizesBeforeBuild(paths.appBuild);
57+
})
58+
.then(previousFileSizes => {
59+
// Remove all content but keep the directory so that
60+
// if you're in it, you don't end up in Trash
61+
fs.emptyDirSync(paths.appBuild);
62+
// Merge with the public folder
63+
copyPublicFolder();
64+
// Start the webpack build
65+
return build(previousFileSizes);
66+
})
67+
.then(
68+
({ stats, previousFileSizes, warnings }) => {
69+
if (warnings.length) {
70+
console.log(chalk.yellow('Compiled with warnings.\n'));
71+
console.log(warnings.join('\n\n'));
72+
console.log(
73+
'\nSearch for the ' +
74+
chalk.underline(chalk.yellow('keywords')) +
75+
' to learn more about each warning.'
76+
);
77+
console.log(
78+
'To ignore, add ' +
79+
chalk.cyan('// eslint-disable-next-line') +
80+
' to the line before.\n'
81+
);
82+
} else {
83+
console.log(chalk.green('Compiled successfully.\n'));
84+
}
85+
86+
console.log('File sizes after gzip:\n');
87+
printFileSizesAfterBuild(
88+
stats,
89+
previousFileSizes,
90+
paths.appBuild,
91+
WARN_AFTER_BUNDLE_GZIP_SIZE,
92+
WARN_AFTER_CHUNK_GZIP_SIZE
93+
);
94+
console.log();
95+
96+
const appPackage = require(paths.appPackageJson);
97+
const publicUrl = paths.publicUrl;
98+
const publicPath = config.output.publicPath;
99+
const buildFolder = path.relative(process.cwd(), paths.appBuild);
100+
printHostingInstructions(
101+
appPackage,
102+
publicUrl,
103+
publicPath,
104+
buildFolder,
105+
useYarn
106+
);
107+
},
108+
err => {
109+
console.log(chalk.red('Failed to compile.\n'));
110+
printBuildError(err);
111+
process.exit(1);
112+
}
113+
)
114+
.catch(err => {
115+
if (err && err.message) {
116+
console.log(err.message);
117+
}
118+
process.exit(1);
119+
});
120+
121+
// Create the production build and print the deployment instructions.
122+
function build(previousFileSizes) {
123+
// We used to support resolving modules according to `NODE_PATH`.
124+
// This now has been deprecated in favor of jsconfig/tsconfig.json
125+
// This lets you use absolute paths in imports inside large monorepos:
126+
if (process.env.NODE_PATH) {
127+
console.log(
128+
chalk.yellow(
129+
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
130+
)
131+
);
132+
console.log();
133+
}
134+
135+
console.log('Creating an optimized production build...');
136+
137+
const compiler = webpack(config);
138+
return new Promise((resolve, reject) => {
139+
compiler.run((err, stats) => {
140+
let messages;
141+
if (err) {
142+
if (!err.message) {
143+
return reject(err);
144+
}
145+
messages = formatWebpackMessages({
146+
errors: [err.message],
147+
warnings: [],
148+
});
149+
} else {
150+
messages = formatWebpackMessages(
151+
stats.toJson({ all: false, warnings: true, errors: true })
152+
);
153+
}
154+
if (messages.errors.length) {
155+
// Only keep the first error. Others are often indicative
156+
// of the same problem, but confuse the reader with noise.
157+
if (messages.errors.length > 1) {
158+
messages.errors.length = 1;
159+
}
160+
return reject(new Error(messages.errors.join('\n\n')));
161+
}
162+
if (
163+
process.env.CI &&
164+
(typeof process.env.CI !== 'string' ||
165+
process.env.CI.toLowerCase() !== 'false') &&
166+
messages.warnings.length
167+
) {
168+
console.log(
169+
chalk.yellow(
170+
'\nTreating warnings as errors because process.env.CI = true.\n' +
171+
'Most CI servers set it automatically.\n'
172+
)
173+
);
174+
return reject(new Error(messages.warnings.join('\n\n')));
175+
}
176+
177+
return resolve({
178+
stats,
179+
previousFileSizes,
180+
warnings: messages.warnings,
181+
});
182+
});
183+
});
184+
}
185+
186+
function copyPublicFolder() {
187+
fs.copySync(paths.appPublic, paths.appBuild, {
188+
dereference: true,
189+
filter: file => file !== paths.appHtml,
190+
});
191+
}
+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
'use strict';
2+
3+
// Do this as the first thing so that any code reading it knows the right env.
4+
process.env.BABEL_ENV = 'development';
5+
process.env.NODE_ENV = 'development';
6+
7+
// Makes the script crash on unhandled rejections instead of silently
8+
// ignoring them. In the future, promise rejections that are not handled will
9+
// terminate the Node.js process with a non-zero exit code.
10+
process.on('unhandledRejection', err => {
11+
throw err;
12+
});
13+
14+
// Ensure environment variables are read.
15+
require('../config/env');
16+
17+
18+
const fs = require('fs');
19+
const chalk = require('react-dev-utils/chalk');
20+
const webpack = require('webpack');
21+
const WebpackDevServer = require('webpack-dev-server');
22+
const clearConsole = require('react-dev-utils/clearConsole');
23+
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
24+
const {
25+
choosePort,
26+
createCompiler,
27+
prepareProxy,
28+
prepareUrls,
29+
} = require('react-dev-utils/WebpackDevServerUtils');
30+
const openBrowser = require('react-dev-utils/openBrowser');
31+
const paths = require('../config/paths');
32+
const configFactory = require('../config/webpack.config');
33+
const createDevServerConfig = require('../config/webpackDevServer.config');
34+
35+
const useYarn = fs.existsSync(paths.yarnLockFile);
36+
const isInteractive = process.stdout.isTTY;
37+
38+
// Warn and crash if required files are missing
39+
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
40+
process.exit(1);
41+
}
42+
43+
// Tools like Cloud9 rely on this.
44+
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
45+
const HOST = process.env.HOST || '0.0.0.0';
46+
47+
if (process.env.HOST) {
48+
console.log(
49+
chalk.cyan(
50+
`Attempting to bind to HOST environment variable: ${chalk.yellow(
51+
chalk.bold(process.env.HOST)
52+
)}`
53+
)
54+
);
55+
console.log(
56+
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
57+
);
58+
console.log(
59+
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
60+
);
61+
console.log();
62+
}
63+
64+
// We require that you explicitly set browsers and do not fall back to
65+
// browserslist defaults.
66+
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
67+
checkBrowsers(paths.appPath, isInteractive)
68+
.then(() => {
69+
// We attempt to use the default port but if it is busy, we offer the user to
70+
// run on a different port. `choosePort()` Promise resolves to the next free port.
71+
return choosePort(HOST, DEFAULT_PORT);
72+
})
73+
.then(port => {
74+
if (port == null) {
75+
// We have not found a port.
76+
return;
77+
}
78+
const config = configFactory('development');
79+
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
80+
const appName = require(paths.appPackageJson).name;
81+
const useTypeScript = fs.existsSync(paths.appTsConfig);
82+
const urls = prepareUrls(protocol, HOST, port);
83+
const devSocket = {
84+
warnings: warnings =>
85+
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
86+
errors: errors =>
87+
devServer.sockWrite(devServer.sockets, 'errors', errors),
88+
};
89+
// Create a webpack compiler that is configured with custom messages.
90+
const compiler = createCompiler({
91+
appName,
92+
config,
93+
devSocket,
94+
urls,
95+
useYarn,
96+
useTypeScript,
97+
webpack,
98+
});
99+
// Load proxy config
100+
const proxySetting = require(paths.appPackageJson).proxy;
101+
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
102+
// Serve webpack assets generated by the compiler over a web server.
103+
const serverConfig = createDevServerConfig(
104+
proxyConfig,
105+
urls.lanUrlForConfig
106+
);
107+
const devServer = new WebpackDevServer(compiler, serverConfig);
108+
// Launch WebpackDevServer.
109+
devServer.listen(port, HOST, err => {
110+
if (err) {
111+
return console.log(err);
112+
}
113+
if (isInteractive) {
114+
clearConsole();
115+
}
116+
117+
// We used to support resolving modules according to `NODE_PATH`.
118+
// This now has been deprecated in favor of jsconfig/tsconfig.json
119+
// This lets you use absolute paths in imports inside large monorepos:
120+
if (process.env.NODE_PATH) {
121+
console.log(
122+
chalk.yellow(
123+
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
124+
)
125+
);
126+
console.log();
127+
}
128+
129+
console.log(chalk.cyan('Starting the development server...\n'));
130+
openBrowser(urls.localUrlForBrowser);
131+
});
132+
133+
['SIGINT', 'SIGTERM'].forEach(function(sig) {
134+
process.on(sig, function() {
135+
devServer.close();
136+
process.exit();
137+
});
138+
});
139+
})
140+
.catch(err => {
141+
if (err && err.message) {
142+
console.log(err.message);
143+
}
144+
process.exit(1);
145+
});
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
// Do this as the first thing so that any code reading it knows the right env.
4+
process.env.BABEL_ENV = 'test';
5+
process.env.NODE_ENV = 'test';
6+
process.env.PUBLIC_URL = '';
7+
8+
// Makes the script crash on unhandled rejections instead of silently
9+
// ignoring them. In the future, promise rejections that are not handled will
10+
// terminate the Node.js process with a non-zero exit code.
11+
process.on('unhandledRejection', err => {
12+
throw err;
13+
});
14+
15+
// Ensure environment variables are read.
16+
require('../config/env');
17+
18+
19+
const jest = require('jest');
20+
const execSync = require('child_process').execSync;
21+
let argv = process.argv.slice(2);
22+
23+
function isInGitRepository() {
24+
try {
25+
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26+
return true;
27+
} catch (e) {
28+
return false;
29+
}
30+
}
31+
32+
function isInMercurialRepository() {
33+
try {
34+
execSync('hg --cwd . root', { stdio: 'ignore' });
35+
return true;
36+
} catch (e) {
37+
return false;
38+
}
39+
}
40+
41+
// Watch unless on CI or explicitly running all tests
42+
if (
43+
!process.env.CI &&
44+
argv.indexOf('--watchAll') === -1 &&
45+
argv.indexOf('--watchAll=false') === -1
46+
) {
47+
// https://github.com/facebook/create-react-app/issues/5210
48+
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49+
argv.push(hasSourceControl ? '--watch' : '--watchAll');
50+
}
51+
52+
53+
jest.run(argv);

‎examples/with-bt-local/src/App.css

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.App {
2+
text-align: center;
3+
}
4+
5+
.App-logo {
6+
height: 40vmin;
7+
}
8+
9+
.App-header {
10+
background-color: #282c34;
11+
min-height: 100vh;
12+
display: flex;
13+
flex-direction: column;
14+
align-items: center;
15+
justify-content: center;
16+
font-size: calc(10px + 2vmin);
17+
color: white;
18+
}
19+
20+
.App-link {
21+
color: #09d3ac;
22+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @jest-environment browserstack
3+
*/
4+
5+
const { By } = require('selenium-webdriver');
6+
7+
describe('ui testing', () => {
8+
const driver = global.__driver__;
9+
10+
it('load the app', async () => {
11+
await driver.get('http://localhost:8080');
12+
13+
const title = await driver.getTitle();
14+
15+
expect(title).toBe('React App');
16+
});
17+
18+
it('page has a link', async () => {
19+
const anchor = await driver.findElement(By.css('.App-link'));
20+
const text = await anchor.getText();
21+
22+
expect(text).toBe('Learn React');
23+
});
24+
});

‎examples/with-bt-local/src/App.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from 'react';
2+
import logo from './logo.svg';
3+
import './App.css';
4+
5+
function App() {
6+
return (
7+
<div className="App">
8+
<header className="App-header">
9+
<img src={logo} className="App-logo" alt="logo" />
10+
<p>
11+
Edit <code>src/App.js</code> and save to reload.
12+
</p>
13+
<a
14+
className="App-link"
15+
href="https://reactjs.org"
16+
target="_blank"
17+
rel="noopener noreferrer"
18+
>
19+
Learn React
20+
</a>
21+
</header>
22+
</div>
23+
);
24+
}
25+
26+
export default App;
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import App from './App';
4+
5+
it('renders without crashing', () => {
6+
const div = document.createElement('div');
7+
ReactDOM.render(<App />, div);
8+
ReactDOM.unmountComponentAtNode(div);
9+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @jest-environment browserstack
3+
*/
4+
const { toMatchImageSnapshot } = require('jest-image-snapshot');
5+
6+
expect.extend({ toMatchImageSnapshot });
7+
8+
describe('ui testing', () => {
9+
const driver = global.__driver__;
10+
11+
it('load the app', async () => {
12+
await driver.get('http://localhost:8080');
13+
14+
const title = await driver.getTitle();
15+
16+
expect(title).toBe('React App');
17+
});
18+
19+
it('take and screenshot and compare', async () => {
20+
const image = await driver.takeScreenshot();
21+
return expect(image).toMatchImageSnapshot();
22+
});
23+
});

‎examples/with-bt-local/src/index.css

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
body {
2+
margin: 0;
3+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4+
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5+
sans-serif;
6+
-webkit-font-smoothing: antialiased;
7+
-moz-osx-font-smoothing: grayscale;
8+
}
9+
10+
code {
11+
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
12+
monospace;
13+
}

‎examples/with-bt-local/src/index.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import ReactDOM from 'react-dom';
3+
import './index.css';
4+
import App from './App';
5+
import * as serviceWorker from './serviceWorker';
6+
7+
ReactDOM.render(<App />, document.getElementById('root'));
8+
9+
// If you want your app to work offline and load faster, you can change
10+
// unregister() to register() below. Note this comes with some pitfalls.
11+
// Learn more about service workers: https://bit.ly/CRA-PWA
12+
serviceWorker.unregister();

‎examples/with-bt-local/src/logo.svg

+1
Loading
+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// This optional code is used to register a service worker.
2+
// register() is not called by default.
3+
4+
// This lets the app load faster on subsequent visits in production, and gives
5+
// it offline capabilities. However, it also means that developers (and users)
6+
// will only see deployed updates on subsequent visits to a page, after all the
7+
// existing tabs open on the page have been closed, since previously cached
8+
// resources are updated in the background.
9+
10+
// To learn more about the benefits of this model and instructions on how to
11+
// opt-in, read https://bit.ly/CRA-PWA
12+
13+
const isLocalhost = Boolean(
14+
window.location.hostname === 'localhost' ||
15+
// [::1] is the IPv6 localhost address.
16+
window.location.hostname === '[::1]' ||
17+
// 127.0.0.1/8 is considered localhost for IPv4.
18+
window.location.hostname.match(
19+
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20+
)
21+
);
22+
23+
export function register(config) {
24+
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25+
// The URL constructor is available in all browsers that support SW.
26+
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27+
if (publicUrl.origin !== window.location.origin) {
28+
// Our service worker won't work if PUBLIC_URL is on a different origin
29+
// from what our page is served on. This might happen if a CDN is used to
30+
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
31+
return;
32+
}
33+
34+
window.addEventListener('load', () => {
35+
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36+
37+
if (isLocalhost) {
38+
// This is running on localhost. Let's check if a service worker still exists or not.
39+
checkValidServiceWorker(swUrl, config);
40+
41+
// Add some additional logging to localhost, pointing developers to the
42+
// service worker/PWA documentation.
43+
navigator.serviceWorker.ready.then(() => {
44+
console.log(
45+
'This web app is being served cache-first by a service ' +
46+
'worker. To learn more, visit https://bit.ly/CRA-PWA'
47+
);
48+
});
49+
} else {
50+
// Is not localhost. Just register service worker
51+
registerValidSW(swUrl, config);
52+
}
53+
});
54+
}
55+
}
56+
57+
function registerValidSW(swUrl, config) {
58+
navigator.serviceWorker
59+
.register(swUrl)
60+
.then(registration => {
61+
registration.onupdatefound = () => {
62+
const installingWorker = registration.installing;
63+
if (installingWorker == null) {
64+
return;
65+
}
66+
installingWorker.onstatechange = () => {
67+
if (installingWorker.state === 'installed') {
68+
if (navigator.serviceWorker.controller) {
69+
// At this point, the updated precached content has been fetched,
70+
// but the previous service worker will still serve the older
71+
// content until all client tabs are closed.
72+
console.log(
73+
'New content is available and will be used when all ' +
74+
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75+
);
76+
77+
// Execute callback
78+
if (config && config.onUpdate) {
79+
config.onUpdate(registration);
80+
}
81+
} else {
82+
// At this point, everything has been precached.
83+
// It's the perfect time to display a
84+
// "Content is cached for offline use." message.
85+
console.log('Content is cached for offline use.');
86+
87+
// Execute callback
88+
if (config && config.onSuccess) {
89+
config.onSuccess(registration);
90+
}
91+
}
92+
}
93+
};
94+
};
95+
})
96+
.catch(error => {
97+
console.error('Error during service worker registration:', error);
98+
});
99+
}
100+
101+
function checkValidServiceWorker(swUrl, config) {
102+
// Check if the service worker can be found. If it can't reload the page.
103+
fetch(swUrl)
104+
.then(response => {
105+
// Ensure service worker exists, and that we really are getting a JS file.
106+
const contentType = response.headers.get('content-type');
107+
if (
108+
response.status === 404 ||
109+
(contentType != null && contentType.indexOf('javascript') === -1)
110+
) {
111+
// No service worker found. Probably a different app. Reload the page.
112+
navigator.serviceWorker.ready.then(registration => {
113+
registration.unregister().then(() => {
114+
window.location.reload();
115+
});
116+
});
117+
} else {
118+
// Service worker found. Proceed as normal.
119+
registerValidSW(swUrl, config);
120+
}
121+
})
122+
.catch(() => {
123+
console.log(
124+
'No internet connection found. App is running in offline mode.'
125+
);
126+
});
127+
}
128+
129+
export function unregister() {
130+
if ('serviceWorker' in navigator) {
131+
navigator.serviceWorker.ready.then(registration => {
132+
registration.unregister();
133+
});
134+
}
135+
}

‎examples/with-bt-local/yarn.lock

+10,259
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.