89c12fa161
* 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
351 lines
10 KiB
JavaScript
Executable File
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),
|
|
];
|