+
+
Workflow archived
+
+ This workflow has been retrieved from archival. Some summary
+ information may be missing.
+
+
- Workflow Name
- {{ workflow.workflowExecutionInfo.type.name }}
@@ -126,7 +135,7 @@ import moment from 'moment';
import { TERMINATE_DEFAULT_ERROR_MESSAGE } from './constants';
import { NOTIFICATION_TYPE_ERROR, NOTIFICATION_TYPE_SUCCESS } from '~constants';
import { getErrorMessage } from '~helpers';
-import { BarLoader, DataViewer, DetailList } from '~components';
+import { BarLoader, DataViewer, DetailList, FeatureFlag } from '~components';
export default {
data() {
@@ -149,6 +158,7 @@ export default {
'bar-loader': BarLoader,
'data-viewer': DataViewer,
'detail-list': DetailList,
+ 'feature-flag': FeatureFlag,
},
computed: {
workflowCloseTime() {
@@ -199,6 +209,14 @@ section.workflow-summary
overflow auto
padding layout-spacing-small
+ .pending-activities {
+ dl.details {
+ dd {
+ white-space: normal;
+ }
+ }
+ }
+
dl:not(.details)
& > div
margin-bottom 1em
diff --git a/client/styles/base.styl b/client/styles/base.styl
index 685e9dd8..b0087d06 100644
--- a/client/styles/base.styl
+++ b/client/styles/base.styl
@@ -65,13 +65,12 @@ a[target="_blank"]::after
table
width 100%
border-collapse collapse
- thead
- border-bottom 2px solid uber-white-60
td, th
padding inline-spacing-medium
th
+ box-shadow: 0px 2px 0 0 uber-white-60;
text-transform uppercase
color black
font-weight 500
diff --git a/client/styles/select.styl b/client/styles/select.styl
index a917fe21..07529723 100644
--- a/client/styles/select.styl
+++ b/client/styles/select.styl
@@ -4,6 +4,11 @@ main .v-select
font-family inherit
color text-color
+ input {
+ left: 0;
+ position: absolute !important;
+ }
+
.dropdown-toggle {
border-radius: 0;
border: input-border;
@@ -15,15 +20,12 @@ main .v-select
.open-indicator
height 24px
- span.selected-tag
+ span.selected-tag {
+ height: 28px;
+ margin-right: 25px;
+ position: relative !important;
one-liner-ellipsis()
-
- &.single.open span.selected-tag
- position static
- opacity 1
-
- &.unsearchable input[type="search"]
- width 20px !important
+ }
.dropdown-toggle button.clear
display none
diff --git a/client/test/index.js b/client/test/index.js
index 0be68a43..162fff58 100644
--- a/client/test/index.js
+++ b/client/test/index.js
@@ -138,7 +138,7 @@ require('./namespace-list.test');
require('./help.test');
require('./workflow-list.test');
require('./workflow.test');
-require('./namespace-config.test');
+require('./namespace-settings.test');
require('./task-list.test');
mocha.run();
diff --git a/client/test/namespace-list.test.js b/client/test/namespace-list.test.js
index 3c1dc4b6..cf5d7739 100644
--- a/client/test/namespace-list.test.js
+++ b/client/test/namespace-list.test.js
@@ -1,4 +1,4 @@
-describe('Namespace search', () => {
+describe('Namespace list', () => {
it('should show a header bar without a breadcrumb or namespace changer', async function test() {
const testEl = new Scenario(this.test).render();
const headerBar = await testEl.waitUntilExists('header.top-bar');
@@ -70,7 +70,7 @@ describe('Namespace search', () => {
.textNodes('dl.details dd')
.should.deep.equal([
'A cool namespace',
- 'ci-test@temporalio.com',
+ 'ci-test@uber.com',
'No',
'21 days',
'Yes',
@@ -167,7 +167,9 @@ describe('Namespace search', () => {
.withWorkflows('open');
await testEl.waitUntilExists('section.workflow-list');
- localStorage.getItem('recent-namespaces').should.equal('["ci-tests","demo"]');
+ localStorage
+ .getItem('recent-namespaces')
+ .should.equal('["ci-tests","demo"]');
});
it('should show a description of recent namespaces when hovered', async function test() {
@@ -194,7 +196,7 @@ describe('Namespace search', () => {
.textNodes('dl.details dd')
.should.deep.equal([
'demo playground',
- 'ci-test@temporalio.com',
+ 'ci-test@uber.com',
'No',
'3 days',
'Yes',
diff --git a/client/test/namespace-config.test.js b/client/test/namespace-settings.test.js
similarity index 85%
rename from client/test/namespace-config.test.js
rename to client/test/namespace-settings.test.js
index 52ed1c2f..6c0ccc20 100644
--- a/client/test/namespace-config.test.js
+++ b/client/test/namespace-settings.test.js
@@ -1,4 +1,4 @@
-describe('Namespace Configuration', () => {
+describe('Namespace Settings', () => {
async function namespaceConfigTest(mochaTest, desc) {
const [testEl, scenario] = new Scenario(mochaTest)
.withNamespace('ci-test')
@@ -6,7 +6,7 @@ describe('Namespace Configuration', () => {
.withNamespaceDescription('ci-test', desc)
.go();
- const configEl = await testEl.waitUntilExists('section.namespace-config');
+ const configEl = await testEl.waitUntilExists('section.namespace-settings');
return [configEl, scenario];
}
@@ -24,6 +24,8 @@ describe('Namespace Configuration', () => {
'Global?',
'Retention Period',
'Emit Metrics',
+ 'History Archival',
+ 'Visibility Archival',
'Failover Version',
'clusters',
]);
@@ -31,10 +33,12 @@ describe('Namespace Configuration', () => {
.textNodes('dl.details dd')
.should.deep.equal([
'A cool namespace',
- 'ci-test@temporalio.com',
+ 'ci-test@uber.com',
'No',
'21 days',
'Yes',
+ 'Enabled',
+ 'Disabled',
'0',
'ci-test-cluster (active)',
]);
diff --git a/client/test/scenario.js b/client/test/scenario.js
index 3ee4905b..35b42d2d 100644
--- a/client/test/scenario.js
+++ b/client/test/scenario.js
@@ -132,6 +132,8 @@ Scenario.prototype.withNamespaceDescription = function withNamespaceDescription(
configuration: {
workflowExecutionRetentionPeriodInDays: 21,
emitMetric: true,
+ historyArchivalStatus: 'ENABLED',
+ visibilityArchivalStatus: 'DISABLED',
},
replicationConfiguration: {
activeClusterName: 'ci-test-cluster',
@@ -161,9 +163,12 @@ Scenario.prototype.withWorkflows = function withWorkflows(
workflows = JSON.parse(JSON.stringify(fixtures.workflows[status]));
}
- const url = `/api/namespaces/${this.namespace}/workflows/${status}?${qs.stringify({
+ const startTimeDays = status === 'open' ? 30 : 21;
+ const url = `/api/namespaces/${
+ this.namespaces
+ }/workflows/${status}?${qs.stringify({
startTime: moment()
- .subtract(21, 'day')
+ .subtract(startTimeDays, 'day')
.startOf('day')
.toISOString(),
endTime: moment()
diff --git a/client/test/workflow-list.test.js b/client/test/workflow-list.test.js
index 243634bd..482f4ef9 100644
--- a/client/test/workflow-list.test.js
+++ b/client/test/workflow-list.test.js
@@ -2,7 +2,12 @@ import moment from 'moment';
import fixtures from './fixtures';
describe('Workflow list', () => {
- async function workflowsTest(mochaTest, initialWorkflows, query, namespaceDesc) {
+ async function workflowsTest(
+ mochaTest,
+ initialWorkflows,
+ query,
+ namespaceDesc
+ ) {
const [testEl, scenario] = new Scenario(mochaTest)
.withNamespace('ci-test')
.startingAt('/namespaces/ci-test/workflows')
@@ -25,21 +30,6 @@ describe('Workflow list', () => {
},
];
- it('should show the namespace with configuration link and workflows breadcrumb in the nav bar', async function test() {
- const [, scenario] = await workflowsTest(this.test);
- const header = scenario.vm.$el.querySelector('header.top-bar');
-
- header.should.have
- .descendant('a.workflows')
- .and.have.class('router-link-active')
- .and.have.attribute('href', '/namespaces/ci-test/workflows');
-
- header.should.have
- .descendant('a.config')
- .and.not.have.class('router-link-active')
- .and.have.attribute('href', 'namespace/ci-test/config');
- });
-
it('should query for open workflows and show the results in a grid', async function test() {
const [workflowsEl] = await workflowsTest(this.test);
const resultsEl = workflowsEl.querySelector('section.results');
@@ -264,7 +254,9 @@ describe('Workflow list', () => {
it('should use query parameters from the URL', async function test() {
const [testEl] = new Scenario(this.test)
.withNamespace('ci-test')
- .startingAt('/namespaces/ci-test/workflows?status=FAILED&workflowName=demo')
+ .startingAt(
+ '/namespaces/ci-test/workflows?status=FAILED&workflowName=demo'
+ )
.withWorkflows('closed', {
status: 'FAILED',
workflowName: 'demo',
diff --git a/client/test/workflow.test.js b/client/test/workflow.test.js
index 6615c954..b5afc456 100644
--- a/client/test/workflow.test.js
+++ b/client/test/workflow.test.js
@@ -38,7 +38,7 @@ describe('Workflow', () => {
scenario.withFullHistory(opts.events);
const summaryEl = await scenario
.render(opts.attach)
- .waitUntilExists('section.workflow section.workflow-summary dl');
+ .waitUntilExists('section.execution section.workflow-summary dl');
return [summaryEl.parentElement, scenario];
}
@@ -1056,7 +1056,7 @@ describe('Workflow', () => {
const [, scenario] = await summaryTest(this.test);
scenario.vm.$el
- .attrValues('section.workflow > nav a', 'href')
+ .attrValues('section.execution > nav a', 'href')
.should.deep.equal([
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/summary',
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/history',
@@ -1064,10 +1064,10 @@ describe('Workflow', () => {
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/query',
]);
scenario.vm.$el
- .querySelector('section.workflow > nav a#nav-link-stack-trace')
+ .querySelector('section.execution > nav a#nav-link-stack-trace')
.should.not.have.property('display', 'none');
scenario.vm.$el
- .querySelector('section.workflow > nav a#nav-link-query')
+ .querySelector('section.execution > nav a#nav-link-query')
.should.not.have.property('display', 'none');
});
@@ -1151,7 +1151,7 @@ describe('Workflow', () => {
const queryEl = await scenario
.render()
- .waitUntilExists('section.workflow section.query');
+ .waitUntilExists('section.execution section.query');
return [queryEl, scenario];
}
@@ -1230,7 +1230,7 @@ describe('Workflow', () => {
});
scenario.vm.$el
- .attrValues('section.workflow > nav a', 'href')
+ .attrValues('section.execution > nav a', 'href')
.should.deep.equal([
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/summary',
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/history',
@@ -1238,14 +1238,15 @@ describe('Workflow', () => {
'/namespaces/ci-test/workflows/email-daily-summaries/emailRun1/query',
]);
scenario.vm.$el
- .querySelector('section.workflow > nav a#nav-link-summary')
+ .querySelector('section.execution > nav a#nav-link-summary')
.should.have.class('router-link-active');
await retry(() => {
scenario.vm.$el.querySelector(
- 'section.workflow > nav a#nav-link-stack-trace'
+ 'section.execution > nav a#nav-link-stack-trace'
+ ).should.not.be.displayed;
+ scenario.vm.$el.querySelector(
+ 'section.execution > nav a#nav-link-query'
).should.not.be.displayed;
- scenario.vm.$el.querySelector('section.workflow > nav a#nav-link-query')
- .should.not.be.displayed;
});
});
diff --git a/jest.config.js b/jest.config.js
index 401b2836..67ef133d 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -187,9 +187,9 @@ module.exports = {
// },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
- // transformIgnorePatterns: [
- // "/node_modules/"
- // ],
+ transformIgnorePatterns: [
+ "/node_modules/(?!lodash-es).+\\.js$"
+ ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
diff --git a/package.json b/package.json
index f5f4c005..2fa6578e 100644
--- a/package.json
+++ b/package.json
@@ -27,11 +27,13 @@
},
"dependencies": {
"@babel/core": "^7.8.3",
+ "@babel/plugin-transform-regenerator": "^7.8.7",
"@babel/polyfill": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@grpc/proto-loader": "^0.5.3",
"babel-loader": "^8.0.6",
"babel-plugin-module-resolver": "^4.0.0",
+ "babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"bluebird": "^3.7.2",
"css-loader": "^0.28.7",
@@ -102,9 +104,11 @@
"eslint-plugin-vue": "^6.1.2",
"fetch-mock": "^5.13.1",
"jest": "^24.9.0",
+ "jest-fetch-mock": "^3.0.3",
"mocha": "^4.0.1",
"mocha-chrome": "^1.1.0",
"nathanboktae-browser-test-utils": "^0.1.0",
+ "node-abort-controller": "^1.0.4",
"prettier": "^1.19.1",
"supertest": "^3.0.0",
"webpack-hot-client": "^1.3.0"
diff --git a/server/middleware/workflow-client.js b/server/middleware/workflow-client.js
index 743c21ea..c99d35d3 100644
--- a/server/middleware/workflow-client.js
+++ b/server/middleware/workflow-client.js
@@ -175,7 +175,7 @@ function WorkflowClient() {
this.client = client;
}
-WorkflowClient.prototype.describeNamespace = async function ({ name }) {
+WorkflowClient.prototype.describeNamespace = async function({ name }) {
const req = { name };
const res = await this.client.describeNamespaceAsync(req);
@@ -183,7 +183,7 @@ WorkflowClient.prototype.describeNamespace = async function ({ name }) {
return uiTransform(res);
};
-WorkflowClient.prototype.listNamespaces = async function ({
+WorkflowClient.prototype.listNamespaces = async function({
pageSize,
nextPageToken,
}) {
@@ -194,7 +194,7 @@ WorkflowClient.prototype.listNamespaces = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.openWorkflows = async function ({
+WorkflowClient.prototype.openWorkflows = async function({
namespace,
startTimeFilter,
typeFilter,
@@ -215,7 +215,7 @@ WorkflowClient.prototype.openWorkflows = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.closedWorkflows = async function ({
+WorkflowClient.prototype.closedWorkflows = async function({
namespace,
startTimeFilter,
typeFilter,
@@ -239,7 +239,7 @@ WorkflowClient.prototype.closedWorkflows = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.listWorkflows = async function ({
+WorkflowClient.prototype.listWorkflows = async function({
namespace,
query,
nextPageToken,
@@ -259,7 +259,7 @@ WorkflowClient.prototype.listWorkflows = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.getHistory = async function ({
+WorkflowClient.prototype.getHistory = async function({
namespace,
nextPageToken,
execution,
@@ -283,7 +283,25 @@ WorkflowClient.prototype.getHistory = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.exportHistory = async function ({
+WorkflowClient.prototype.archivedWorkflows = async function({
+ namespace,
+ nextPageToken,
+ query,
+ pageSize = 100,
+}) {
+ const req = {
+ namespace,
+ nextPageToken,
+ query,
+ pageSize,
+ };
+
+ const res = await this.client.listArchivedWorkflowExecutionsAsync(req);
+
+ return uiTransform(res);
+};
+
+WorkflowClient.prototype.exportHistory = async function({
namespace,
execution,
nextPageToken,
@@ -299,7 +317,7 @@ WorkflowClient.prototype.exportHistory = async function ({
return cliTransform(res);
};
-WorkflowClient.prototype.queryWorkflow = async function ({
+WorkflowClient.prototype.queryWorkflow = async function({
namespace,
execution,
query,
@@ -314,7 +332,7 @@ WorkflowClient.prototype.queryWorkflow = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.terminateWorkflow = async function ({
+WorkflowClient.prototype.terminateWorkflow = async function({
namespace,
execution,
reason,
@@ -330,7 +348,7 @@ WorkflowClient.prototype.terminateWorkflow = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.signalWorkflow = async function ({
+WorkflowClient.prototype.signalWorkflow = async function({
namespace,
execution,
signal,
@@ -346,7 +364,7 @@ WorkflowClient.prototype.signalWorkflow = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.describeWorkflow = async function ({
+WorkflowClient.prototype.describeWorkflow = async function({
namespace,
execution,
}) {
@@ -360,7 +378,7 @@ WorkflowClient.prototype.describeWorkflow = async function ({
return uiTransform(res);
};
-WorkflowClient.prototype.describeTaskList = async function ({
+WorkflowClient.prototype.describeTaskList = async function({
namespace,
taskList,
taskListType,
diff --git a/server/routes.js b/server/routes.js
index 4fb5c5d2..1d62266f 100644
--- a/server/routes.js
+++ b/server/routes.js
@@ -86,6 +86,48 @@ router.get(
}
);
+const buildQueryString = (
+ startTime,
+ endTime,
+ { status, workflowId, workflowName }
+) => {
+ return [
+ `CloseTime >= "${startTime.toISOString()}"`,
+ `CloseTime <= "${endTime.toISOString()}"`,
+ status && `CloseStatus = "${status}"`,
+ workflowId && `WorkflowID = "${workflowId}"`,
+ workflowName && `WorkflowType = "${workflowName}"`,
+ ]
+ .filter(subQuery => !!subQuery)
+ .join(' and ');
+};
+
+router.get('/api/namespaces/:namespace/workflows/archived', async function(
+ ctx
+) {
+ const { namespace } = ctx.params;
+ const { nextPageToken, ...query } = ctx.query || {};
+ let queryString;
+
+ if (query.queryString) {
+ queryString = query.queryString;
+ } else {
+ const startTime = moment(query.startTime || NaN);
+ const endTime = moment(query.endTime || NaN);
+
+ ctx.assert(startTime.isValid() && endTime.isValid(), 400);
+ queryString = buildQueryString(startTime, endTime, query);
+ }
+
+ ctx.body = await wfClient.archivedWorkflows({
+ namespace,
+ nextPageToken: nextPageToken
+ ? Buffer.from(nextPageToken, 'base64')
+ : undefined,
+ query: queryString,
+ });
+});
+
router.get(
'/api/namespaces/:namespace/workflows/:workflowId/:runId/export',
async function(ctx) {
@@ -184,52 +226,102 @@ router.post(
}
);
-router.get('/api/namespaces/:namespace/workflows/:workflowId/:runId', async function(
- ctx
-) {
- const { namespace, workflowId, runId } = ctx.params;
-
- ctx.body = await wfClient.describeWorkflow({
- namespace,
- execution: { workflowId, runId },
- });
-});
+router.get(
+ '/api/namespaces/:namespace/workflows/:workflowId/:runId',
+ async function(ctx) {
+ const { namespace, workflowId, runId } = ctx.params;
-router.get('/api/namespaces/:namespace/task-lists/:taskList/pollers', async function(
- ctx
-) {
- const { namespace, taskList } = ctx.params;
- const descTaskList = async taskListType =>
- (
- await wfClient.describeTaskList({
+ try {
+ ctx.body = await wfClient.describeWorkflow({
namespace,
- taskList: { name: taskList },
- taskListType,
- })
- ).pollers || [];
-
- const r = type => (o, poller) => {
- const i = o[poller.identity] || {};
-
- o[poller.identity] = {
- lastAccessTime:
- !i.lastAccessTime || i.lastAccessTime < poller.lastAccessTime
- ? poller.lastAccessTime
- : i.lastAccessTime,
- taskListTypes: i.taskListTypes ? i.taskListTypes.concat([type]) : [type],
- };
+ execution: { workflowId, runId },
+ });
+ } catch (error) {
+ if (error.name !== 'NotFoundError') {
+ throw error;
+ }
+
+ const archivedHistoryResponse = await wfClient.getHistory();
+ const archivedHistoryEvents = mapHistoryResponse(
+ archivedHistoryResponse.history
+ );
- return o;
- };
+ if (!archivedHistoryEvents.length) {
+ throw error;
+ }
- const activityL = await descTaskList('Activity'),
- decisionL = await descTaskList('Decision');
+ const { runId, workflowId } = ctx.params;
- ctx.body = activityL.reduce(
- r('activity'),
- decisionL.reduce(r('decision'), {})
- );
-});
+ const {
+ timestamp: startTime,
+ details: {
+ taskList,
+ executionStartToCloseTimeoutSeconds,
+ taskStartToCloseTimeoutSeconds,
+ workflowType: type,
+ },
+ } = archivedHistoryEvents[0];
+
+ ctx.body = {
+ executionConfiguration: {
+ taskList,
+ executionStartToCloseTimeoutSeconds,
+ taskStartToCloseTimeoutSeconds,
+ },
+ workflowExecutionInfo: {
+ execution: {
+ runId,
+ workflowId,
+ },
+ isArchived: true,
+ startTime,
+ type,
+ },
+ pendingActivities: null,
+ pendingChildren: null,
+ };
+ }
+ }
+);
+
+router.get(
+ '/api/namespaces/:namespace/task-lists/:taskList/pollers',
+ async function(ctx) {
+ const { namespace, taskList } = ctx.params;
+ const descTaskList = async taskListType =>
+ (
+ await wfClient.describeTaskList({
+ namespace,
+ taskList: { name: taskList },
+ taskListType,
+ })
+ ).pollers || [];
+
+ const r = type => (o, poller) => {
+ const i = o[poller.identity] || {};
+
+ o[poller.identity] = {
+ lastAccessTime:
+ !i.lastAccessTime || i.lastAccessTime < poller.lastAccessTime
+ ? poller.lastAccessTime
+ : i.lastAccessTime,
+ taskListTypes: i.taskListTypes
+ ? i.taskListTypes.concat([type])
+ : [type],
+ };
+
+ return o;
+ };
+
+ const activityL = await descTaskList('Activity'),
+ decisionL = await descTaskList('Decision');
+
+ ctx.body = activityL.reduce(
+ r('activity'),
+ decisionL.reduce(r('decision'), {})
+ );
+ }
+);
router.get('/health', ctx => (ctx.body = 'OK'));
diff --git a/server/test/history.test.js b/server/test/history.test.js
index b90ddaf8..138d5ff0 100644
--- a/server/test/history.test.js
+++ b/server/test/history.test.js
@@ -100,6 +100,7 @@ describe('Workflow History', function() {
},
maximumPageSize: 100,
nextPageToken: null,
+ skipArchival: null,
waitForNewEvent: null
})
@@ -121,7 +122,7 @@ describe('Workflow History', function() {
return {
history: { events: [] },
- nextPageToken: new Buffer('page3')
+ nextPageToken: new Buffer('page3'),
}
}
@@ -132,7 +133,8 @@ describe('Workflow History', function() {
.expect({
archived: null,
history: { events: [] },
- nextPageToken: 'cGFnZTM='
+ nextPageToken: 'cGFnZTM=',
+ rawHistory: null,
})
})
@@ -155,7 +157,7 @@ describe('Workflow History', function() {
it('should transform Long numbers to JavaScript numbers, Long dates to ISO date strings, and line-delimited JSON buffers to JSON', function() {
this.test.GetWorkflowExecutionHistory = ({ getRequest }) => ({
history: { events: wfHistoryThrift },
- nextPageToken: new Buffer('page2')
+ nextPageToken: new Buffer('page2'),
})
return request()
@@ -164,7 +166,8 @@ describe('Workflow History', function() {
.expect({
archived: null,
history: { events: wfHistoryJson },
- nextPageToken: 'cGFnZTI='
+ nextPageToken: 'cGFnZTI=',
+ rawHistory: null,
})
})
diff --git a/test/setup.js b/test/setup.js
index 52956a73..2307dd7b 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -1,7 +1,17 @@
// polyfills for web browser to node
+import 'babel-polyfill';
+import AbortController from 'node-abort-controller'
import atob from 'atob';
import { injectMomentDurationFormat, jsonTryParse } from '~helpers';
+import { enableFetchMocks } from 'jest-fetch-mock'
global.atob = atob;
global.JSON.tryParse = jsonTryParse;
injectMomentDurationFormat();
+
+if (!window.AbortController) {
+ window.AbortController = AbortController;
+}
+
+enableFetchMocks();
+window.fetch = global.fetch;
diff --git a/webpack.config.js b/webpack.config.js
index 2b574e42..39eec69c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -6,10 +6,12 @@ const
extractStylus = 'css-loader?sourceMap!stylus-loader',
development = !['production', 'ci'].includes(process.env.NODE_ENV)
+require('babel-polyfill');
+
module.exports = {
devtool: 'source-map',
entry: [
- process.env.TEST_RUN ? 'babel-polyfill' : '',
+ 'babel-polyfill',
path.join(__dirname, process.env.TEST_RUN ? 'client/test/index' : 'client/main')
].filter(x => x),
output: {
@@ -40,7 +42,7 @@ module.exports = {
test: /\.js$/,
loader: 'babel-loader',
options: {
- configFile: path.resolve(__dirname, '.babelrc'),
+ configFile: path.resolve(__dirname, 'babel.config.js'),
},
},
{
@@ -51,7 +53,7 @@ module.exports = {
js: {
loader: 'babel-loader',
options: {
- configFile: path.resolve(__dirname, '.babelrc'),
+ configFile: path.resolve(__dirname, 'babel.config.js'),
},
},
stylus: ExtractTextPlugin.extract({