Files
gas-react-sheets/webpack.config.js
T
Elisha Nuchi 89c12fa161 Set up end to end tests (#121)
* initial e2e test setup with jest and puppeteer

* Update package-lock.json

* WIP e2e setup

* finish setting up end to end tests

* extend open addon time

* build local version and change background color

* update pink image snapshot

* hide no "done" callback jest lint error

* first draft of integration test CI automation

* add .env file during CI

* add puppeteer and update .env

* try mac-os

* try separate build step

* add local deploy step

* extend time

* print body text

* set user agent

* rearrange console logs

* remove console logs

* console page

* try loading in own window

* install mkcert in CI

* fix indentation

* test local body text

* bump time

* revert to loading in add on

* bump time

* print page body

* add stealth plugin and update puppeteer

* update jest and jest-puppeteer versions to latest

* use custom jest-puppeteer environment

allows connecting to Google account using stealth plugin

* update threshold

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test console

* switch to clasp 2.3.1

* increase timeout

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update package.json

* try mac os 10.15

* Update integration-tests.yaml

* run node 12,14

* Update global-teardown.js

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* sudo mkcert

* Update integration-tests.yaml

* try running not in bash

* try windows 2016

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* add timeout

* test recovery email

* bypass account recovery page

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test New-SelfSignedCertificate

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* add powershell script

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test script

* Update integration-tests.yaml

* add generate-cert.ps1

* restructure test files

* Update generate-cert.ps1

* bump failure threshold

* fix install mkcert

* remove consoles

* Update package.json

* Update integration-tests.yaml

* try forceExit flag

* test exec in global setup

* restructure

* Update integration-tests.yaml

* remove ?. null check

* add 4 OSs: macos-10.15, macos-latest, windows-2019, windows-latest

* test mac 10.15

* Update integration-tests.yaml

* use all OSs

* -Keyexportpolicy Exportable

* Update generate-cert.ps1

* Update integration-tests.yaml

* update os

* -KeyExportPolicy 'Exportable'

* Update integration-tests.yaml

* update test readme

* Update integration-tests.yaml

* add non-extended integration test

* add non-extended snapshots

* update snapshot names

* change name

* add basic integration tests pipeline file

* Update local-development.test.js

* update os's

* test s3 image reporter

* allowSizeMismatch: true

* update test local url to localhost:3000/dialog-demo-bootstrap.html

* try medium font render hinting

* Update jest-puppeteer.config.js

* update screenshots

* check resolution

* open page when wds is ready

* change screen resolution

* change to 2560 1440 resolution

* test 790 width

* change back to 800

* test all mac versions

* rename container

* skip s3 image reporting if missing env variables

* try windows

* add OS specific tests

* fix image names

* Update image-reporter.js

* Update package.json

* upload windows

* Update image-reporter.js

* test new font

* try all OSs

* remove OS-specific test setup

* update extended snapshots

* rename tests

* Update integration-tests-extended.yaml

* Update integration-tests-basic.yaml

* extend timeout

* Update jest.config.js

* Update README.md

* Update local-development.test.js

* revert to 0.04 threshold

* add comment

* remove push trigger for basic test

* extend timeout
2022-03-12 22:51:30 -05:00

351 lines
10 KiB
JavaScript
Executable File

/*********************************
* import webpack plugins
********************************/
const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const GasPlugin = require('gas-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const DynamicCdnWebpackPlugin = require('dynamic-cdn-webpack-plugin');
const moduleToCdn = require('module-to-cdn');
/*********************************
* set up environment variables
********************************/
const dotenv = require('dotenv').config();
const parsed = dotenv.error ? {} : dotenv.parsed;
const envVars = parsed || {};
const PORT = envVars.PORT || 3000;
envVars.NODE_ENV = process.env.NODE_ENV;
envVars.PORT = PORT;
const isProd = process.env.NODE_ENV === 'production';
/*********************************
* define entrypoints
********************************/
// our destination directory
const destination = path.resolve(__dirname, 'dist');
// define server paths
const serverEntry = './src/server/index.js';
// define appsscript.json file path
const copyAppscriptEntry = './appsscript.json';
// define live development dialog paths
const devDialogEntry = './dev/index.js';
// define client entry points and output names
const clientEntrypoints = [
{
name: 'CLIENT - Dialog Demo',
entry: './src/client/dialog-demo/index.js',
filename: 'dialog-demo', // we'll add the .html suffix to these
template: './src/client/dialog-demo/index.html',
},
{
name: 'CLIENT - Dialog Demo Bootstrap',
entry: './src/client/dialog-demo-bootstrap/index.js',
filename: 'dialog-demo-bootstrap',
template: './src/client/dialog-demo-bootstrap/index.html',
},
{
name: 'CLIENT - Sidebar About Page',
entry: './src/client/sidebar-about-page/index.js',
filename: 'sidebar-about-page',
template: './src/client/sidebar-about-page/index.html',
},
];
// define certificate locations
// see "npm run setup:https" script in package.json
const keyPath = path.resolve(__dirname, './certs/key.pem');
const certPath = path.resolve(__dirname, './certs/cert.pem');
const pfxPath = path.resolve(__dirname, './certs/cert.pfx'); // if needed for Windows
/*********************************
* Declare settings
********************************/
// webpack settings for copying files to the destination folder
const copyFilesConfig = {
name: 'COPY FILES - appsscript.json',
mode: 'production', // unnecessary for this config, but removes console warning
entry: copyAppscriptEntry,
output: {
path: destination,
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: copyAppscriptEntry,
to: destination,
},
],
}),
],
};
// webpack settings used by both client and server
const sharedClientAndServerConfig = {
context: __dirname,
};
// webpack settings used by all client entrypoints
const clientConfig = {
...sharedClientAndServerConfig,
mode: isProd ? 'production' : 'development',
output: {
path: destination,
// this file will get added to the html template inline
// and should be put in .claspignore so it is not pushed
filename: 'main.js',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
},
module: {
rules: [
// typescript config
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
},
],
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
// we could add support for scss here
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
],
},
};
// DynamicCdnWebpackPlugin settings
// these settings help us load 'react', 'react-dom' and the packages defined below from a CDN
// see https://github.com/enuchi/React-Google-Apps-Script#adding-new-libraries-and-packages
const DynamicCdnWebpackPluginConfig = {
// set "verbose" to true to print console logs on CDN usage while webpack builds
verbose: false,
resolver: (packageName, packageVersion, options) => {
const packageSuffix = isProd ? '.min.js' : '.js';
const moduleDetails = moduleToCdn(packageName, packageVersion, options);
if (moduleDetails) {
return moduleDetails;
}
// "name" should match the package being imported
// "var" is important to get right -- this should be the exposed global. Look up "webpack externals" for info.
switch (packageName) {
case 'react-transition-group':
return {
name: packageName,
var: 'ReactTransitionGroup',
version: packageVersion,
url: `https://unpkg.com/react-transition-group@${packageVersion}/dist/react-transition-group${packageSuffix}`,
};
case 'react-bootstrap':
return {
name: packageName,
var: 'ReactBootstrap',
version: packageVersion,
url: `https://unpkg.com/react-bootstrap@${packageVersion}/dist/react-bootstrap${packageSuffix}`,
};
default:
return null;
}
},
};
// webpack settings used by each client entrypoint defined at top
const clientConfigs = clientEntrypoints.map(clientEntrypoint => {
return {
...clientConfig,
name: clientEntrypoint.name,
entry: clientEntrypoint.entry,
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify(envVars),
}),
new HtmlWebpackPlugin({
template: clientEntrypoint.template,
filename: `${clientEntrypoint.filename}${isProd ? '' : '-impl'}.html`,
inlineSource: '^[^(//)]+.(js|css)$', // embed all js and css inline, exclude packages with '//' for dynamic cdn insertion
}),
// add the generated js code to the html file inline
new HtmlWebpackInlineSourcePlugin(),
// this plugin allows us to add dynamically load packages from a CDN
new DynamicCdnWebpackPlugin(DynamicCdnWebpackPluginConfig),
],
};
});
const gasWebpackDevServerPath = require.resolve(
'google-apps-script-webpack-dev-server'
);
// webpack settings for devServer https://webpack.js.org/configuration/dev-server/
const devServer = {
port: PORT,
https: true,
// run our own route to serve the package google-apps-script-webpack-dev-server
before: app => {
// this '/gas/' path needs to match the path loaded in the iframe in dev/index.js
app.get('/gas/*', (req, res) => {
res.setHeader('Content-Type', 'text/html');
fs.createReadStream(gasWebpackDevServerPath).pipe(res);
});
},
};
if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
// use key and cert settings only if they are found
devServer.https = {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certPath),
};
}
// If mkcert -install cannot be used on Windows machines (in pipeline, for example), the
// script at test/generate-cert.ps1 can be used to create a .pfx cert
if (fs.existsSync(pfxPath)) {
// use pfx file if it's found
devServer.https = {
pfx: fs.readFileSync(pfxPath),
passphrase: 'abc123',
};
}
// webpack settings for the development client wrapper
const devClientConfigs = clientEntrypoints.map(clientEntrypoint => {
envVars.FILENAME = clientEntrypoint.filename;
return {
...clientConfig,
name: `DEVELOPMENT: ${clientEntrypoint.name}`,
entry: devDialogEntry,
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify(envVars),
}),
new HtmlWebpackPlugin({
template: './dev/index.html',
// this should match the html files we load in src/server/ui.js
filename: `${clientEntrypoint.filename}.html`,
inlineSource: '^[^(//)]+.(js|css)$', // embed all js and css inline, exclude packages with '//' for dynamic cdn insertion
}),
new HtmlWebpackInlineSourcePlugin(),
new DynamicCdnWebpackPlugin({}),
],
};
});
// webpack settings used by the server-side code
const serverConfig = {
...sharedClientAndServerConfig,
name: 'SERVER',
// server config can't use 'development' mode
// https://github.com/fossamagna/gas-webpack-plugin/issues/135
mode: isProd ? 'production' : 'none',
entry: serverEntry,
output: {
filename: 'code.js',
path: destination,
libraryTarget: 'this',
},
resolve: {
extensions: ['.ts', '.js', '.json'],
},
module: {
rules: [
// typescript config
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'ts-loader',
},
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
],
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
sourceMap: true,
terserOptions: {
// ecma 5 is needed to support Rhino "DEPRECATED_ES5" runtime
// can use ecma 6 if V8 runtime is used
ecma: 5,
warnings: false,
parse: {},
compress: {
properties: false,
},
mangle: false,
module: false,
output: {
beautify: true,
// support custom function autocompletion
// https://developers.google.com/apps-script/guides/sheets/functions
comments: /@customfunction/,
},
},
}),
],
},
plugins: [
new webpack.DefinePlugin({
// replace any env variables in client-side code like PORT and NODE_ENV with actual values
'process.env': JSON.stringify(envVars),
'process.env.NODE_ENV': JSON.stringify(
isProd ? 'production' : 'development'
),
}),
new GasPlugin(),
],
};
module.exports = [
// 1. Copy appsscript.json to destination,
// 2. Set up webpack dev server during development
// Note: devServer settings are only read in the first element when module.exports is an array
{ ...copyFilesConfig, ...(isProd ? {} : { devServer }) },
// 3. Create the server bundle
serverConfig,
// 4. Create one client bundle for each client entrypoint.
...clientConfigs,
// 5. Create a development dialog bundle for each client entrypoint during development.
...(isProd ? [] : devClientConfigs),
];