Merge pull request #4629 from mhils/update-web-deps

Update Web Dependencies
This commit is contained in:
Maximilian Hils 2021-06-09 18:07:36 +02:00 committed by GitHub
commit de0951462d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3567 additions and 4447 deletions

View File

@ -1,4 +0,0 @@
{
"presets": ["es2015", "react"],
"plugins": ["transform-class-properties", "transform-object-rest-spread"]
}

View File

@ -1,6 +0,0 @@
{
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
}
}

View File

@ -9,7 +9,7 @@ and activate your virtualenv environment before proceeding.**
## Testing
- Run `yarn run test` to run the testsuite.
- Run `yarn test` to run the test suite.
## Architecture

4
web/babel.config.js Normal file
View File

@ -0,0 +1,4 @@
/* This currently is only used for jest. We use esbuild for actual bundling. */
module.exports = {
presets: ['@babel/preset-react', '@babel/preset-env', '@babel/preset-typescript'],
};

View File

@ -1,30 +0,0 @@
var conf = {
src: "src/",
dist: "../mitmproxy/tools/web",
static: "../mitmproxy/tools/web/static",
js: {
// Don't package these in the vendor distribution
vendor_excludes: [
"bootstrap" // We only use Bootstrap's CSS.
],
// Package these as well as the dependencies
vendor_includes: [
],
app: 'src/js/app',
eslint: ["src/js/**/*.js", "!src/js/filt/filt.js"]
},
css: {
vendor: ["src/css/vendor.less"],
app: ["src/css/app.less"]
},
copy: [
"src/images/**", "src/fonts/fontawesome-webfont.*"
],
templates: [
"src/templates/*"
],
peg: ["src/js/filt/filt.peg"]
};
module.exports = conf;

View File

@ -1,228 +1,118 @@
var path = require('path');
const gulp = require("gulp");
const gulpEsbuild = require('gulp-esbuild');
const less = require("gulp-less");
const livereload = require("gulp-livereload");
const cleanCSS = require('gulp-clean-css');
const notify = require("gulp-notify");
const compilePeg = require("gulp-peg");
const plumber = require("gulp-plumber");
const sourcemaps = require('gulp-sourcemaps');
const through = require("through2");
var packagejs = require('./package.json');
var conf = require('./conf.js');
// Sorted alphabetically!
var babelify = require('babelify');
var envify = require('envify/custom');
var browserify = require('browserify');
var gulp = require("gulp");
var eslint = require('gulp-eslint');
var less = require("gulp-less");
var livereload = require("gulp-livereload");
var cleanCSS = require('gulp-clean-css');
var notify = require("gulp-notify");
var peg = require("gulp-peg");
var plumber = require("gulp-plumber");
var rename = require("gulp-rename");
var sourcemaps = require('gulp-sourcemaps');
var gutil = require("gulp-util");
var _ = require('lodash');
var uglifyify = require('uglifyify');
var buffer = require('vinyl-buffer');
var source = require('vinyl-source-stream');
var watchify = require('watchify');
var vendor_packages = _.difference(
_.union(
_.keys(packagejs.dependencies),
conf.js.vendor_includes
),
conf.js.vendor_excludes
);
const noop = () => through.obj();
var handleError = {errorHandler: notify.onError("Error: <%= error.message %>")};
/*
* Sourcemaps are a wonderful way to develop directly from the chrome devtools.
* However, generating correct sourcemaps is a huge PITA, especially on Windows.
* Fixing this upstream is tedious as apparently nobody really cares and
* a single misbehaving transform breaks everything.
* Thus, we just manually fix all paths.
*/
function fixSourceMaps(file) {
file.sourceMap.sources = file.sourceMap.sources.map(function (x) {
return path.relative(".", x).split(path.sep).join('/');
});
return "/";
}
// Browserify fails for paths starting with "..".
function fixBrowserifySourceMaps(file) {
file.sourceMap.sources = file.sourceMap.sources.map((x) => {
return x.replace("src/js/node_modules", "node_modules");
});
return fixSourceMaps(file);
}
function fixLessSourceMaps(file) {
file.sourceMap.sources = file.sourceMap.sources.map((x) => {
if(!x.startsWith("..")){
return "../src/css/" + x;
}
return x.replace("src/js/node_modules", "node_modules");
});
return fixSourceMaps(file);
}
function styles(files, dev){
function styles(files, dev) {
return gulp.src(files)
.pipe(dev ? plumber(handleError) : gutil.noop())
.pipe(dev ? plumber(handleError) : noop())
.pipe(sourcemaps.init())
.pipe(less())
.pipe(dev ? gutil.noop() : cleanCSS())
.pipe(sourcemaps.write(".", {sourceRoot: fixLessSourceMaps}))
.pipe(gulp.dest(conf.static))
.pipe(dev ? noop() : cleanCSS())
.pipe(sourcemaps.write(".", {sourceRoot: '/src/css'}))
.pipe(gulp.dest("../mitmproxy/tools/web/static"))
.pipe(livereload({auto: false}));
}
gulp.task("styles-app-dev", function () {
return styles(conf.css.app, true);
});
gulp.task("styles-vendor-dev", function () {
return styles(conf.css.vendor, true);
});
gulp.task("styles-app-prod", function () {
return styles(conf.css.app, false);
});
gulp.task("styles-vendor-prod", function () {
return styles(conf.css.vendor, false);
});
function styles_vendor_prod() {
return styles("src/css/vendor.less", false)
}
function styles_vendor_dev() {
return styles("src/css/vendor.less", true)
}
function styles_app_prod() {
return styles("src/css/app.less", false)
}
function styles_app_dev() {
return styles("src/css/app.less", true)
}
function buildScript(bundler, filename, dev) {
if (dev) {
bundler = watchify(bundler);
} else {
bundler = bundler.transform(envify({ _: 'purge', NODE_ENV: 'production' }), { global: true });
bundler = bundler.transform({global: true}, uglifyify);
}
function rebundle() {
return bundler.bundle()
.on('error', function(error) {
gutil.log(error + '\n' + error.codeFrame);
this.emit('end');
})
.pipe(dev ? plumber(handleError) : gutil.noop())
.pipe(source('bundle.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(rename(filename))
.pipe(sourcemaps.write('.', {sourceRoot: fixBrowserifySourceMaps}))
.pipe(gulp.dest(conf.static))
function esbuild(dev) {
return gulp.src('src/js/app.jsx').pipe(
gulpEsbuild({
outfile: 'app.js',
sourcemap: true,
sourceRoot: "/",
minify: !dev,
keepNames: true,
bundle: true,
}))
.pipe(gulp.dest("../mitmproxy/tools/web/static"))
.pipe(livereload({auto: false}));
}
// listen for an update and run rebundle
bundler.on('update', rebundle);
bundler.on('log', gutil.log);
bundler.on('error', gutil.log);
// run it once the first time buildScript is called
return rebundle();
}
function vendor_stream(dev) {
var bundler = browserify({
entries: [],
debug: true,
cache: {}, // required for watchify
packageCache: {} // required for watchify
});
for (var vp of vendor_packages) {
bundler.require(vp);
}
return buildScript(bundler, "vendor.js", dev);
function scripts_dev() {
return esbuild(true);
}
gulp.task("scripts-vendor-dev", function () {
return vendor_stream(true);
});
gulp.task("scripts-vendor-prod", function () {
return vendor_stream(false);
});
function app_stream(dev) {
var bundler = browserify({
entries: [conf.js.app],
debug: true,
extensions: ['.jsx'],
cache: {}, // required for watchify
packageCache: {} // required for watchify
});
for (var vp of vendor_packages) {
bundler.external(vp);
}
bundler = bundler.transform(babelify);
return buildScript(bundler, "app.js", dev);
function scripts_prod() {
return esbuild(false);
}
gulp.task('scripts-app-dev', function () {
return app_stream(true);
});
gulp.task('scripts-app-prod', function () {
return app_stream(false);
});
const copy_src = ["src/images/**", "src/fonts/fontawesome-webfont.*"];
gulp.task("eslint", function () {
return gulp.src(conf.js.eslint)
function copy() {
return gulp.src(copy_src, {base: "src/"})
.pipe(gulp.dest("../mitmproxy/tools/web/static"));
}
const template_src = "src/templates/*";
function templates() {
return gulp.src(template_src, {base: "src/"})
.pipe(gulp.dest("../mitmproxy/tools/web"));
}
const peg_src = "src/js/filt/filt.peg";
function peg() {
return gulp.src(peg_src, {base: "src/"})
.pipe(plumber(handleError))
.pipe(eslint())
.pipe(eslint.format())
});
gulp.task("copy", function () {
return gulp.src(conf.copy, {base: conf.src})
.pipe(gulp.dest(conf.static));
});
gulp.task('templates', function(){
return gulp.src(conf.templates, {base: conf.src})
.pipe(gulp.dest(conf.dist));
});
gulp.task("peg", function () {
return gulp.src(conf.peg, {base: conf.src})
.pipe(plumber(handleError))
.pipe(peg())
.pipe(compilePeg())
.pipe(gulp.dest("src/"));
});
}
gulp.task(
"dev",
gulp.series(
"copy",
"styles-vendor-dev",
"styles-app-dev",
"scripts-vendor-dev",
"peg",
"scripts-app-dev",
"templates"
)
);
gulp.task(
"prod",
gulp.series(
"copy",
"styles-vendor-prod",
"styles-app-prod",
"scripts-vendor-prod",
"peg",
"scripts-app-prod",
"templates"
)
const dev = gulp.parallel(
copy,
styles_vendor_dev,
styles_app_dev,
peg,
scripts_dev,
templates
);
gulp.task("default", gulp.series(
"dev",
function watch() {
const prod = gulp.parallel(
copy,
styles_vendor_prod,
styles_app_prod,
peg,
scripts_prod,
templates
);
exports.dev = dev;
exports.prod = prod;
exports.default = function watch() {
const opts = {ignoreInitial: false};
livereload.listen({auto: true});
gulp.watch(["src/css/vendor*"], gulp.series("styles-vendor-dev"));
gulp.watch(["src/css/**"], gulp.series("styles-app-dev"));
gulp.watch(conf.templates, gulp.series("templates"));
gulp.watch(conf.peg, gulp.series("peg"));
gulp.watch(["src/js/**"], gulp.series("eslint"));
// other JS is handled by watchify.
gulp.watch(conf.copy, gulp.series("copy"));
})
);
gulp.watch(["src/css/vendor*"], opts, styles_vendor_dev);
gulp.watch(["src/css/**"], opts, styles_app_dev);
gulp.watch(["src/js/**"], opts, scripts_dev);
gulp.watch(template_src, opts, templates);
gulp.watch(peg_src, opts, peg);
gulp.watch(copy_src, opts, copy);
}

19
web/jest.config.js Normal file
View File

@ -0,0 +1,19 @@
process.env.TZ = 'UTC';
module.exports = {
"testEnvironment": "jsdom",
"testRegex": "__tests__/.*Spec.(js|ts)x?$",
"roots": [
"<rootDir>/src/js"
],
"unmockedModulePathPatterns": [
"react"
],
"coverageDirectory": "./coverage",
"coveragePathIgnorePatterns": [
"<rootDir>/src/js/filt/filt.js"
],
"collectCoverageFrom": [
"src/js/**/*.{js,jsx,ts,tsx}"
]
};

View File

@ -2,71 +2,46 @@
"name": "mitmproxy",
"private": true,
"scripts": {
"test": "TZ=UTC jest --coverage",
"test": "jest --coverage",
"build": "gulp prod",
"start": "gulp"
},
"jest": {
"testRegex": "__tests__/.*Spec.js$",
"roots": [
"<rootDir>/src/js"
],
"unmockedModulePathPatterns": [
"react"
],
"coverageDirectory": "./coverage",
"coveragePathIgnorePatterns": [
"<rootDir>/src/js/filt/filt.js"
],
"collectCoverageFrom": [
"src/js/**/*.{js,jsx}"
]
},
"dependencies": {
"bootstrap": "^3.3.7",
"classnames": "^2.2.5",
"lodash": "^4.17.4",
"classnames": "^2.3.1",
"lodash": "^4.17.21",
"mock-xmlhttprequest": "^1.1.0",
"prop-types": "^15.6.0",
"react": "16.0.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-codemirror": "^1.0.0",
"react-dom": "16.0.0",
"react-redux": "^5.0.6",
"react-test-renderer": "16.0.0",
"redux": "^3.7.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.4",
"react-test-renderer": "^17.0.2",
"redux": "^4.1.0",
"redux-logger": "^3.0.6",
"redux-mock-store": "^1.3.0",
"redux-thunk": "^2.2.0",
"shallowequal": "^1.0.2",
"stable": "^0.1.6"
"redux-mock-store": "^1.5.4",
"redux-thunk": "^2.3.0",
"shallowequal": "^1.1.0",
"stable": "^0.1.8"
},
"devDependencies": {
"babel-core": "^6.26.0",
"babel-eslint": "^8.0.1",
"babel-jest": "^21.2.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babelify": "^8.0.0",
"browserify": "^14.5.0",
"envify": "^4.1.0",
"eslint": "^4.9.0",
"@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4",
"@babel/preset-react": "^7.13.13",
"@babel/preset-typescript": "^7.13.0",
"babel-jest": "^27.0.2",
"esbuild": "^0.12.8",
"esbuild-jest": "^0.5.0",
"gulp": "^4.0.2",
"gulp-clean-css": "^4.2.0",
"gulp-eslint": "^6.0.0",
"gulp-clean-css": "^4.3.0",
"gulp-esbuild": "^0.8.1",
"gulp-less": "^4.0.1",
"gulp-livereload": "^4.0.2",
"gulp-notify": "^3.2.0",
"gulp-notify": "^4.0.0",
"gulp-peg": "^0.2.0",
"gulp-plumber": "^1.2.1",
"gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^2.6.5",
"gulp-util": "^3.0.8",
"jest": "^21.2.1",
"uglifyify": "^4.0.4",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"watchify": "^3.11.1"
"gulp-sourcemaps": "^3.0.0",
"jest": "^27.0.4",
"through2": "^4.0.2"
}
}

View File

@ -6,7 +6,7 @@ import { ViewServer, ViewImage, PureViewServer, Edit } from '../../../components
import { TFlow, TStore } from '../../ducks/tutils'
import mockXMLHttpRequest from 'mock-xmlhttprequest'
global.XMLHttpRequest = mockXMLHttpRequest
window.XMLHttpRequest = mockXMLHttpRequest
let tflow = new TFlow()
describe('ViewImage Component', () => {

View File

@ -26,7 +26,7 @@ exports[`ContentTooLarge Components should render correctly 1`] = `
>
<button
className="btn btn-xs btn-warning pull-right"
onClick={[Function]}
onClick={[MockFunction]}
>
Display anyway
</button>

View File

@ -6,9 +6,7 @@ exports[`ShowFullContentButton Component should render correctly 1`] = `
<div>
<div
className="view-all-content-btn btn-xs btn btn-default"
disabled={undefined}
onClick={[Function]}
title={undefined}
>
Show full content
</div>

View File

@ -5,7 +5,7 @@ import { TStore, TFlow } from '../ducks/tutils'
import { Provider } from 'react-redux'
import mockXMLHttpRequest from 'mock-xmlhttprequest'
global.XMLHttpRequest = mockXMLHttpRequest
window.XMLHttpRequest = mockXMLHttpRequest
describe('ContentView Component', () => {
let store = TStore()

View File

@ -2,10 +2,9 @@ jest.mock('../../components/EventLog/EventList')
import React from 'react'
import renderer from 'react-test-renderer'
import TestUtils from 'react-dom/test-utils'
import EventLog, { PureEventLog } from '../../components/EventLog'
import { Provider } from 'react-redux'
import { TStore } from '../ducks/tutils'
import EventLog, {PureEventLog} from '../../components/EventLog'
import {Provider} from 'react-redux'
import {TStore} from '../ducks/tutils'
window.addEventListener = jest.fn()
window.removeEventListener = jest.fn()
@ -27,31 +26,31 @@ describe('EventLog Component', () => {
debugToggleButton.props.onClick()
})
provider = TestUtils.renderIntoDocument(
provider = renderer.create(
<Provider store={store}><EventLog/></Provider>)
let eventLog = TestUtils.findRenderedComponentWithType(provider, PureEventLog),
mockEvent = { preventDefault: jest.fn() }
let eventLog = provider.root.findByType(PureEventLog),
mockEvent = {preventDefault: jest.fn()}
it('should handle DragStart', () => {
eventLog.onDragStart(mockEvent)
eventLog.instance.onDragStart(mockEvent)
expect(mockEvent.preventDefault).toBeCalled()
expect(window.addEventListener).toBeCalledWith('mousemove', eventLog.onDragMove)
expect(window.addEventListener).toBeCalledWith('mouseup', eventLog.onDragStop)
expect(window.addEventListener).toBeCalledWith('dragend', eventLog.onDragStop)
expect(window.addEventListener).toBeCalledWith('mousemove', eventLog.instance.onDragMove)
expect(window.addEventListener).toBeCalledWith('mouseup', eventLog.instance.onDragStop)
expect(window.addEventListener).toBeCalledWith('dragend', eventLog.instance.onDragStop)
mockEvent.preventDefault.mockClear()
})
it('should handle DragMove', () => {
eventLog.onDragMove(mockEvent)
eventLog.instance.onDragMove(mockEvent)
expect(mockEvent.preventDefault).toBeCalled()
mockEvent.preventDefault.mockClear()
})
console.error = jest.fn() // silent the error.
it('should handle DragStop', () => {
eventLog.onDragStop(mockEvent)
eventLog.instance.onDragStop(mockEvent)
expect(mockEvent.preventDefault).toBeCalled()
expect(window.removeEventListener).toBeCalledWith('mousemove', eventLog.onDragMove)
expect(window.removeEventListener).toBeCalledWith('mousemove', eventLog.instance.onDragMove)
})
})

View File

@ -15,36 +15,36 @@ describe('FlowTable Component', () => {
it('should render correctly', () => {
let provider = renderer.create(
<Provider store={store}>
<FlowTable onSelect={selectFn} flows={[tflow]}/>
<FlowTable selectFlow={selectFn} flows={[tflow]}/>
</Provider>),
tree = provider.toJSON()
expect(tree).toMatchSnapshot()
})
let provider = TestUtils.renderIntoDocument(
let provider = renderer.create(
<Provider store={store} >
<FlowTable onSelect={selectFn} flows={[tflow]}/>
<FlowTable selectFlow={selectFn} flows={[tflow]}/>
</Provider>),
flowTable = TestUtils.findRenderedComponentWithType(provider, FlowTable)
flowTable = provider.root.findByType(FlowTable)
it('should handle componentWillUnmount', () => {
flowTable.componentWillUnmount()
expect(window.addEventListener).toBeCalledWith('resize', flowTable.onViewportUpdate)
flowTable.instance.UNSAFE_componentWillUnmount()
expect(window.addEventListener).toBeCalledWith('resize', flowTable.instance.onViewportUpdate)
})
it('should handle componentDidUpdate', () => {
// flowTable.shouldScrollIntoView == false
expect(flowTable.componentDidUpdate()).toEqual(undefined)
expect(flowTable.instance.componentDidUpdate()).toEqual(undefined)
// rowTop - headHeight < viewportTop
flowTable.shouldScrollIntoView = true
flowTable.componentDidUpdate()
flowTable.instance.shouldScrollIntoView = true
flowTable.instance.componentDidUpdate()
// rowBottom > viewportTop + viewportHeight
flowTable.shouldScrollIntoView = true
flowTable.componentDidUpdate()
flowTable.instance.shouldScrollIntoView = true
flowTable.instance.componentDidUpdate()
})
it('should handle componentWillReceiveProps', () => {
flowTable.componentWillReceiveProps({selected: tflow})
expect(flowTable.shouldScrollIntoView).toBeTruthy()
flowTable.instance.UNSAFE_componentWillReceiveProps({selected: tflow})
expect(flowTable.instance.shouldScrollIntoView).toBeTruthy()
})
})

View File

@ -1,12 +1,11 @@
jest.mock('../../../components/ContentView')
jest.mock('../../../components/ContentView', () => () => null)
import React from 'react'
import renderer from 'react-test-renderer'
import TestUtils from 'react-dom/test-utils'
import { Request, Response, ErrorView } from '../../../components/FlowView/Messages'
import { Provider } from 'react-redux'
import { TFlow, TStore } from '../../ducks/tutils'
import { updateEdit } from '../../../ducks/ui/flow'
import { parseUrl } from '../../../flow/utils'
import {ErrorView, Request, Response} from '../../../components/FlowView/Messages'
import {Provider} from 'react-redux'
import {TFlow, TStore} from '../../ducks/tutils'
import {updateEdit} from '../../../ducks/ui/flow'
import {parseUrl} from '../../../flow/utils'
import ContentView from '../../../components/ContentView'
import ContentViewOptions from '../../../components/ContentView/ContentViewOptions'
import Headers from '../../../components/FlowView/Headers'
@ -20,7 +19,9 @@ store.getState().ui.flow.modifiedFlow = false
describe('Request Component', () => {
afterEach(() => {store.clearActions()})
afterEach(() => {
store.clearActions()
})
it('should render correctly', () => {
let provider = renderer.create(
@ -32,45 +33,47 @@ describe('Request Component', () => {
expect(tree).toMatchSnapshot()
})
let provider = TestUtils.renderIntoDocument(
let provider = renderer.create(
<Provider store={store}>
<Request/>
</Provider>),
valueEditors = TestUtils.scryRenderedComponentsWithType(provider, ValueEditor)
valueEditors = provider.root.findAllByType(ValueEditor)
it('should handle done on flow request method', () => {
let valueEditor = valueEditors[0]
valueEditor.props.onDone('foo')
expect(store.getActions()).toEqual([updateEdit({ request: { method: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({request: {method: 'foo'}})])
})
it('should handle done on flow request url', () => {
let valueEditor = valueEditors[1],
url = 'http://foo/bar'
valueEditor.props.onDone(url)
expect(store.getActions()).toEqual([updateEdit({ request: { path: '', ...parseUrl(url)}})])
expect(store.getActions()).toEqual([updateEdit({request: {path: '', ...parseUrl(url)}})])
})
it('should handle done on flow request http version', () => {
let valueEditor = valueEditors[2]
valueEditor.props.onDone('HTTP/9.9')
expect(store.getActions()).toEqual([updateEdit({ request: { http_version: 'HTTP/9.9' }})])
expect(store.getActions()).toEqual([updateEdit({request: {http_version: 'HTTP/9.9'}})])
})
it('should handle change on flow request header', () => {
let headers = TestUtils.scryRenderedComponentsWithType(provider, Headers).filter(headers => headers.props.type === 'headers')[0]
let headers = provider.root.findAllByType(Headers).filter(headers => headers.props.type === 'headers')[0]
headers.props.onChange('foo')
expect(store.getActions()).toEqual([updateEdit({ request: { headers: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({request: {headers: 'foo'}})])
})
it('should handle change on flow request contentView', () => {
let contentView = TestUtils.findRenderedComponentWithType(provider, ContentView)
let contentView = provider.root.findByType(ContentView)
contentView.props.onContentChange('foo')
expect(store.getActions()).toEqual([updateEdit({ request: { content: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({request: {content: 'foo'}})])
})
it('should handle uploadContent on flow request ContentViewOptions', () => {
let contentViewOptions = TestUtils.findRenderedComponentWithType(provider, ContentViewOptions)
// The line below shouldn't have .type, this is a workaround for https://github.com/facebook/react/issues/17301.
// If this test breaks, just remove it.
let contentViewOptions = provider.root.findByType(ContentViewOptions.type)
contentViewOptions.props.uploadContent('foo')
expect(fetch).toBeCalled()
fetch.mockClear()
@ -78,7 +81,9 @@ describe('Request Component', () => {
})
describe('Response Component', () => {
afterEach(() => {store.clearActions()})
afterEach(() => {
store.clearActions()
})
it('should render correctly', () => {
let provider = renderer.create(
@ -90,44 +95,46 @@ describe('Response Component', () => {
expect(tree).toMatchSnapshot()
})
let provider = TestUtils.renderIntoDocument(
let provider = renderer.create(
<Provider store={store}>
<Response/>
</Provider>),
valueEditors = TestUtils.scryRenderedComponentsWithType(provider, ValueEditor)
valueEditors = provider.root.findAllByType(ValueEditor)
it('should handle done on flow response http version', () => {
let valueEditor = valueEditors[0]
valueEditor.props.onDone('HTTP/9.9')
expect(store.getActions()).toEqual([updateEdit({ response: { http_version: 'HTTP/9.9' }})])
expect(store.getActions()).toEqual([updateEdit({response: {http_version: 'HTTP/9.9'}})])
})
it('should handle done on flow response status code', () => {
let valueEditor = valueEditors[1]
valueEditor.props.onDone('404')
expect(store.getActions()).toEqual([updateEdit({ response: { code: parseInt('404') }})])
expect(store.getActions()).toEqual([updateEdit({response: {code: parseInt('404')}})])
})
it('should handle done on flow response reason', () => {
let valueEdiotr = valueEditors[2]
valueEdiotr.props.onDone('foo')
expect(store.getActions()).toEqual([updateEdit( { response: { msg: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({response: {msg: 'foo'}})])
})
it('should handle change on flow response headers', () => {
let headers = TestUtils.scryRenderedComponentsWithType(provider, Headers).filter(headers => headers.props.type === 'headers')[0]
let headers = provider.root.findAllByType(Headers).filter(headers => headers.props.type === 'headers')[0]
headers.props.onChange('foo')
expect(store.getActions()).toEqual([updateEdit( { response: { headers: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({response: {headers: 'foo'}})])
})
it('should handle change on flow response ContentView', () => {
let contentView = TestUtils.findRenderedComponentWithType(provider, ContentView)
let contentView = provider.root.findByType(ContentView)
contentView.props.onContentChange('foo')
expect(store.getActions()).toEqual([updateEdit( { response: { content: 'foo' }})])
expect(store.getActions()).toEqual([updateEdit({response: {content: 'foo'}})])
})
it('should handle updateContent on flow response ContentViewOptions', () => {
let contentViewOptions = TestUtils.findRenderedComponentWithType(provider, ContentViewOptions)
// The line below shouldn't have .type, this is a workaround for https://github.com/facebook/react/issues/17301.
// If this test breaks, just remove it.
let contentViewOptions = provider.root.findByType(ContentViewOptions.type)
contentViewOptions.props.uploadContent('foo')
expect(fetch).toBeCalled()
fetch.mockClear()

View File

@ -3,7 +3,6 @@
exports[`HeaderEditor Component should render correctly 1`] = `
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "foo",
@ -31,7 +30,6 @@ exports[`Headers Component should handle correctly 1`] = `
>
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "k1",
@ -57,7 +55,6 @@ exports[`Headers Component should handle correctly 1`] = `
>
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "v1",
@ -80,7 +77,6 @@ exports[`Headers Component should handle correctly 1`] = `
>
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "k2",
@ -106,7 +102,6 @@ exports[`Headers Component should handle correctly 1`] = `
>
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "",

View File

@ -41,7 +41,6 @@ exports[`Request Component should render correctly 1`] = `
<div>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "GET",
@ -54,12 +53,10 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
 
<div
className="inline-input readonly has-success"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "http://address:22/path",
@ -72,12 +69,10 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
 
<div
className="inline-input readonly has-success"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "HTTP/1.1",
@ -90,7 +85,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</div>
</div>
@ -104,7 +98,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "header",
@ -117,7 +110,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -130,7 +122,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "qvalue",
@ -143,7 +134,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>
@ -153,7 +143,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "content-length",
@ -166,7 +155,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -179,7 +167,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "7",
@ -192,7 +179,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>
@ -210,7 +196,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "trailer",
@ -223,7 +208,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -236,7 +220,6 @@ exports[`Request Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "qvalue",
@ -249,7 +232,6 @@ exports[`Request Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>
@ -361,7 +343,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly has-success"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "HTTP/1.1",
@ -374,12 +355,10 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
 
<div
className="inline-input readonly has-success"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "200",
@ -392,12 +371,10 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
 
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "OK",
@ -410,7 +387,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</div>
<table
@ -423,7 +399,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "header-response",
@ -436,7 +411,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -449,7 +423,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "svalue",
@ -462,7 +435,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>
@ -472,7 +444,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "content-length",
@ -485,7 +456,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -498,7 +468,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "7",
@ -511,7 +480,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>
@ -529,7 +497,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "trailer",
@ -542,7 +509,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
<span
className="header-colon"
@ -555,7 +521,6 @@ exports[`Response Component should render correctly 1`] = `
>
<div
className="inline-input readonly"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "qvalue",
@ -568,7 +533,6 @@ exports[`Response Component should render correctly 1`] = `
onKeyDown={[Function]}
onMouseDown={[Function]}
onPaste={[Function]}
tabIndex={undefined}
/>
</td>
</tr>

View File

@ -31,10 +31,8 @@ exports[`FileMenu Component should render correctly 1`] = `
<li>
<a
className={undefined}
href="#"
onClick={[Function]}
title={undefined}
>
<i
className="fa fa-fw fa-folder-open"

View File

@ -24,7 +24,6 @@ exports[`FilterInput Component should render correctly 1`] = `
onKeyDown={[Function]}
placeholder="bar"
type="text"
value={undefined}
/>
</div>
`;

View File

@ -10,7 +10,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[r]eplay flow"
>
@ -21,7 +20,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
</div>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[D]uplicate flow"
>
@ -33,7 +31,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
<div
className="btn btn-default"
disabled={true}
onClick={undefined}
title="revert changes to flow [V]"
>
<i
@ -43,7 +40,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
</div>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[d]elete flow"
>
@ -67,7 +63,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="download"
>
@ -92,7 +87,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
<div
className="btn btn-default"
disabled={true}
onClick={undefined}
title="[a]ccept intercepted flow"
>
<i
@ -103,7 +97,6 @@ exports[`FlowMenu Component should connect to state 1`] = `
<div
className="btn btn-default"
disabled={true}
onClick={undefined}
title="kill intercepted flow [x]"
>
<i
@ -131,7 +124,6 @@ exports[`FlowMenu Component should render correctly with flow 1`] = `
>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[r]eplay flow"
>
@ -142,7 +134,6 @@ exports[`FlowMenu Component should render correctly with flow 1`] = `
</div>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[D]uplicate flow"
>
@ -164,7 +155,6 @@ exports[`FlowMenu Component should render correctly with flow 1`] = `
</div>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="[d]elete flow"
>
@ -188,7 +178,6 @@ exports[`FlowMenu Component should render correctly with flow 1`] = `
>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="download"
>

View File

@ -22,7 +22,7 @@ exports[`MenuToggle Component should render correctly 1`] = `
<label>
<input
checked={true}
onChange={[Function]}
onChange={[MockFunction]}
type="checkbox"
/>
<p>

View File

@ -10,7 +10,6 @@ exports[`OptionMenu Component should render correctly 1`] = `
>
<div
className="btn btn-default"
disabled={undefined}
onClick={[Function]}
title="Open Options"
>

View File

@ -23,9 +23,9 @@ describe('ValidateEditor Component', () => {
isValid: s => s.length == 3,
content: "bar"
}
validateEditor.componentWillReceiveProps(mockProps)
validateEditor.UNSAFE_componentWillReceiveProps(mockProps)
expect(validateEditor.state.valid).toBeTruthy()
validateEditor.componentWillReceiveProps({...mockProps, content: "bars"})
validateEditor.UNSAFE_componentWillReceiveProps({...mockProps, content: "bars"})
expect(validateEditor.state.valid).toBeFalsy()
})

View File

@ -3,7 +3,6 @@
exports[`ValidateEditor Component should render correctly 1`] = `
<div
className="inline-input editable has-success"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "foo",

View File

@ -3,7 +3,6 @@
exports[`ValueEditor Component should render correctly 1`] = `
<div
className="inline-input editable"
contentEditable={undefined}
dangerouslySetInnerHTML={
Object {
"__html": "foo",

View File

@ -4,8 +4,6 @@ exports[`Button Component should be able to be disabled 1`] = `
<div
className="classname btn btn-default"
disabled="true"
onClick={undefined}
title={undefined}
>
<a>
foo
@ -16,7 +14,6 @@ exports[`Button Component should be able to be disabled 1`] = `
exports[`Button Component should render correctly 1`] = `
<div
className="classname btn btn-default"
disabled={undefined}
onClick={[Function]}
title="title"
>

View File

@ -3,7 +3,7 @@
exports[`ToggleButton Component should render correctly 1`] = `
<div
className="btn btn-toggle btn-primary"
onClick={[Function]}
onClick={[MockFunction]}
>
<i
className="fa fa-fw fa-check-square-o"

View File

@ -12,7 +12,7 @@ describe('Autoscroll', () => {
this.state = { vScroll: calcVScroll() }
}
componentWillUpdate() {
UNSAFE_componentWillUpdate() {
mockFn("foo")
}
@ -31,7 +31,7 @@ describe('Autoscroll', () => {
viewport = ReactDOM.findDOMNode(autoScroll)
viewport.scrollTop = 10
Object.defineProperty(viewport, "scrollHeight", { value: 10, writable: true })
autoScroll.componentWillUpdate()
autoScroll.UNSAFE_componentWillUpdate()
expect(mockFn).toBeCalledWith("foo")
Object.defineProperty(viewport, "scrollHeight", { value: 0, writable: true })

View File

@ -26,7 +26,7 @@ const store = createStore(
)
useUrlState(store)
if (MITMWEB_STATIC) {
if (window.MITMWEB_STATIC) {
window.backend = new StaticBackend(store)
} else {
window.backend = new WebSocketBackend(store)

View File

@ -32,11 +32,11 @@ class FlowTable extends React.Component {
this.onViewportUpdate = this.onViewportUpdate.bind(this)
}
componentWillMount() {
UNSAFE_componentWillMount() {
window.addEventListener('resize', this.onViewportUpdate)
}
componentWillUnmount() {
UNSAFE_componentWillUnmount() {
window.removeEventListener('resize', this.onViewportUpdate)
}
@ -69,7 +69,7 @@ class FlowTable extends React.Component {
}
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.selected && nextProps.selected !== this.props.selected) {
this.shouldScrollIntoView = true
}
@ -77,11 +77,11 @@ class FlowTable extends React.Component {
onViewportUpdate() {
const viewport = ReactDOM.findDOMNode(this)
const viewportTop = viewport.scrollTop
const viewportTop = viewport.scrollTop || 0
const vScroll = calcVScroll({
viewportTop,
viewportHeight: viewport.offsetHeight,
viewportHeight: viewport.offsetHeight || 0,
itemCount: this.props.flows.length,
rowHeight: this.props.rowHeight,
})
@ -103,7 +103,7 @@ class FlowTable extends React.Component {
<FlowTableHead />
</thead>
<tbody>
<tr style={{ height: vScroll.paddingTop }}></tr>
<tr style={{ height: vScroll.paddingTop }}/>
{flows.slice(vScroll.start, vScroll.end).map(flow => (
<FlowRow
key={flow.id}
@ -113,7 +113,7 @@ class FlowTable extends React.Component {
onSelect={this.props.selectFlow}
/>
))}
<tr style={{ height: vScroll.paddingBottom }}></tr>
<tr style={{ height: vScroll.paddingBottom }}/>
</tbody>
</table>
</div>

View File

@ -150,8 +150,8 @@ export default function Details({ flow }) {
{flow.server_conn.address &&
[
<h4>Server Connection</h4>,
<ConnectionInfo conn={flow.server_conn}/>
<h4 key="sc">Server Connection</h4>,
<ConnectionInfo key="sc-ci" conn={flow.server_conn}/>
]
}

View File

@ -14,7 +14,7 @@ export default class FilterDocs extends Component {
this.state = { doc: FilterDocs.doc }
}
componentWillMount() {
componentDidMount() {
if (!FilterDocs.xhr) {
FilterDocs.xhr = fetchApi('/filter-help').then(response => response.json())
FilterDocs.xhr.catch(() => {

View File

@ -106,7 +106,7 @@ export default class FilterInput extends Component {
return (
<div className={classnames('filter-input input-group', { 'has-error': !this.isValid() })}>
<span className="input-group-addon">
<i className={'fa fa-fw fa-' + type} style={{ color }}></i>
<i className={'fa fa-fw fa-' + type} style={{ color }}/>
</span>
<input
type="text"
@ -123,7 +123,7 @@ export default class FilterInput extends Component {
<div className="popover bottom"
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}>
<div className="arrow"></div>
<div className="arrow"/>
<div className="popover-content">
{this.getDesc()}
</div>

View File

@ -11,7 +11,7 @@ const stopPropagation = e => {
}
}
BooleanOption.PropTypes = {
BooleanOption.propTypes = {
value: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
}
@ -30,7 +30,7 @@ function BooleanOption({ value, onChange, ...props }) {
)
}
StringOption.PropTypes = {
StringOption.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
}
@ -52,7 +52,7 @@ function Optional(Component) {
}
}
NumberOption.PropTypes = {
NumberOption.propTypes = {
value: PropTypes.number.isRequired,
onChange: PropTypes.func.isRequired,
}
@ -66,7 +66,7 @@ function NumberOption({ value, onChange, ...props }) {
)
}
ChoicesOption.PropTypes = {
ChoicesOption.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
}
@ -86,8 +86,8 @@ export function ChoicesOption({ value, onChange, choices, ...props }) {
)
}
StringSequenceOption.PropTypes = {
value: PropTypes.string.isRequired,
StringSequenceOption.propTypes = {
value: PropTypes.arrayOf(PropTypes.string).isRequired,
onChange: PropTypes.func.isRequired,
}
function StringSequenceOption({ value, onChange, ...props }) {

View File

@ -21,7 +21,7 @@ export default class ValidateEditor extends Component {
this.onDone = this.onDone.bind(this)
}
componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({ valid: nextProps.isValid(nextProps.content) })
}

View File

@ -1,5 +1,5 @@
import React from 'react'
export default function HideInStatic({ children }) {
return global.MITMWEB_STATIC ? null : [children]
return window.MITMWEB_STATIC ? null : [children]
}

View File

@ -8,9 +8,10 @@ export default Component => Object.assign(class AutoScrollWrapper extends Compon
static displayName = Component.name;
componentWillUpdate() {
UNSAFE_componentWillUpdate() {
const viewport = ReactDOM.findDOMNode(this);
this[symShouldStick] = viewport.scrollTop && isAtBottom(viewport);
super.UNSAFE_componentWillUpdate && super.UNSAFE_componentWillUpdate();
super.componentWillUpdate && super.componentWillUpdate();
}

View File

@ -8,7 +8,6 @@
<link rel="stylesheet" href="/static/app.css"/>
<link rel="icon" href="/static/images/favicon.ico" type="image/x-icon"/>
<script src="/static/static.js"></script>
<script src="/static/vendor.js"></script>
<script src="/static/app.js"></script>
</head>
<body>

File diff suppressed because it is too large Load Diff