revert browser newux changes (#5714)

This commit is contained in:
Kanagaraj M
2018-03-27 01:19:12 +05:30
committed by Dee Koder
parent 35e64573fa
commit 19451e374a
186 changed files with 4507 additions and 16033 deletions

View File

@@ -21,29 +21,40 @@ import logo from "../../img/logo.svg"
export const AboutModal = ({ serverInfo, hideAbout }) => {
const { version, memory, platform, runtime } = serverInfo
return (
<Modal className="about" animation={false} show={true} onHide={hideAbout}>
<i className="close" onClick={hideAbout} />
<div className="about__content">
<div className="about__logo hidden-xs">
<img src={logo} alt="" />
<Modal
className="modal-about modal-dark"
animation={false}
show={true}
onHide={hideAbout}
>
<button className="close" onClick={hideAbout}>
<span>×</span>
</button>
<div className="ma-inner">
<div className="mai-item hidden-xs">
<a href="https://minio.io" target="_blank">
<img className="maii-logo" src={logo} alt="" />
</a>
</div>
<div className="about__info">
<div className="about__item">
<strong>Version</strong>
<small>{version}</small>
</div>
<div className="about__item">
<strong>Memory</strong>
<small>{memory}</small>
</div>
<div className="about__item">
<strong>Platform</strong>
<small>{platform}</small>
</div>
<div className="about__item">
<strong>Runtime</strong>
<small>{runtime}</small>
</div>
<div className="mai-item">
<ul className="maii-list">
<li>
<div>Version</div>
<small>{version}</small>
</li>
<li>
<div>Memory</div>
<small>{memory}</small>
</li>
<li>
<div>Platform</div>
<small>{platform}</small>
</li>
<li>
<div>Runtime</div>
<small>{runtime}</small>
</li>
</ul>
</div>
</div>
</Modal>

View File

@@ -15,21 +15,24 @@
*/
import React from "react"
import classNames from "classnames"
import { connect } from "react-redux"
import SideBar from "./SideBar"
import MainContent from "./MainContent"
import AlertContainer from "../alert/AlertContainer"
const Aux = props => props.children
class Browser extends React.Component {
render() {
return (
<Aux>
<div
className={classNames({
"file-explorer": true
})}
>
<SideBar />
<MainContent />
<AlertContainer />
</Aux>
</div>
)
}
}

View File

@@ -81,61 +81,62 @@ export class BrowserDropdown extends React.Component {
render() {
const { serverInfo } = this.props
return (
<Dropdown pullRight id="toolbar-menu">
<Dropdown.Toggle
noCaret
className="toolbar__item zmdi zmdi-more-vert"
/>
<Dropdown.Menu>
<li>
<a target="_blank" href="https://github.com/minio/minio">
Github <i className="zmdi zmdi-github" />
</a>
</li>
<li>
<a href="" onClick={this.fullScreen}>
Fullscreen <i className="zmdi zmdi-fullscreen" />
</a>
</li>
<li>
<a target="_blank" href="https://docs.minio.io/">
Documentation <i className="zmdi zmdi-assignment" />
</a>
</li>
<li>
<a target="_blank" href="https://slack.minio.io">
Ask for help <i className="zmdi zmdi-help" />
</a>
</li>
<li>
<a href="" id="show-about" onClick={this.showAbout.bind(this)}>
About <i className="zmdi zmdi-info" />
</a>
{this.state.showAboutModal && (
<AboutModal
serverInfo={serverInfo}
hideAbout={this.hideAbout.bind(this)}
/>
)}
</li>
<li>
<a href="" onClick={this.showChangePassword.bind(this)}>
Change Password <i className="zmdi zmdi-settings" />
</a>
{this.state.showChangePasswordModal && (
<ChangePasswordModal
serverInfo={serverInfo}
hideChangePassword={this.hideChangePassword.bind(this)}
/>
)}
</li>
<li>
<a href="" id="logout" onClick={this.logout}>
Sign Out <i className="zmdi zmdi-sign-in" />
</a>
</li>
</Dropdown.Menu>
</Dropdown>
<li>
<Dropdown pullRight id="top-right-menu">
<Dropdown.Toggle noCaret>
<i className="fa fa-reorder" />
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<li>
<a target="_blank" href="https://github.com/minio/minio">
Github <i className="fa fa-github" />
</a>
</li>
<li>
<a href="" onClick={this.fullScreen}>
Fullscreen <i className="fa fa-expand" />
</a>
</li>
<li>
<a target="_blank" href="https://docs.minio.io/">
Documentation <i className="fa fa-book" />
</a>
</li>
<li>
<a target="_blank" href="https://slack.minio.io">
Ask for help <i className="fa fa-question-circle" />
</a>
</li>
<li>
<a href="" id="show-about" onClick={this.showAbout.bind(this)}>
About <i className="fa fa-info-circle" />
</a>
{this.state.showAboutModal && (
<AboutModal
serverInfo={serverInfo}
hideAbout={this.hideAbout.bind(this)}
/>
)}
</li>
<li>
<a href="" onClick={this.showChangePassword.bind(this)}>
Change Password <i className="fa fa-cog" />
</a>
{this.state.showChangePasswordModal && (
<ChangePasswordModal
serverInfo={serverInfo}
hideChangePassword={this.hideChangePassword.bind(this)}
/>
)}
</li>
<li>
<a href="" id="logout" onClick={this.logout}>
Sign Out <i className="fa fa-sign-out" />
</a>
</li>
</Dropdown.Menu>
</Dropdown>
</li>
)
}
}

View File

@@ -26,6 +26,7 @@ import {
ModalHeader,
OverlayTrigger
} from "react-bootstrap"
import InputGroup from "./InputGroup"
export class ChangePasswordModal extends React.Component {
constructor(props) {
@@ -113,55 +114,51 @@ export class ChangePasswordModal extends React.Component {
render() {
const { hideChangePassword } = this.props
return (
<Modal bsSize="sm" animation={false} show={true} className="settings">
<Modal.Header>Change Password</Modal.Header>
<Modal.Body className="m-t-20">
<div className="form-group">
<label className="form-group__label">Access key</label>
<input
type="text"
value={this.state.accessKey}
onChange={this.accessKeyChange.bind(this)}
id="accessKey"
className="form-group__field"
name="accesskey"
required="required"
autoComplete="false"
readonly={this.state.keysReadOnly}
/>
<i className="form-group__helper" />
</div>
<div className="form-group">
<label className="form-group__label">Secret key</label>
<input
type={this.state.secretKeyVisible ? "text" : "password"}
value={this.state.secretKey}
onChange={this.secretKeyChange.bind(this)}
id="secretKey"
className="form-group__field"
name="secretKey"
required="required"
autoComplete="false"
readonly={this.state.keysReadOnly}
/>
<i className="form-group__helper" />
<i
onClick={this.secretKeyVisible.bind(
this,
!this.state.secretKeyVisible
)}
className={
"form-group__addon zmdi " +
(this.state.secretKeyVisible ? "zmdi-eye-off" : "zmdi-eye")
}
/>
</div>
</Modal.Body>
<Modal bsSize="sm" animation={false} show={true}>
<ModalHeader>Change Password</ModalHeader>
<ModalBody className="m-t-20">
<InputGroup
value={this.state.accessKey}
onChange={this.accessKeyChange.bind(this)}
id="accessKey"
label="Access Key"
name="accesskey"
type="text"
spellCheck="false"
required="required"
autoComplete="false"
align="ig-left"
readonly={this.state.keysReadOnly}
/>
<i
onClick={this.secretKeyVisible.bind(
this,
!this.state.secretKeyVisible
)}
className={
"toggle-password fa fa-eye " +
(this.state.secretKeyVisible ? "toggled" : "")
}
/>
<InputGroup
value={this.state.secretKey}
onChange={this.secretKeyChange.bind(this)}
id="secretKey"
label="Secret Key"
name="accesskey"
type={this.state.secretKeyVisible ? "text" : "password"}
spellCheck="false"
required="required"
autoComplete="false"
align="ig-left"
readonly={this.state.keysReadOnly}
/>
</ModalBody>
<div className="modal-footer">
<button
id="generate-keys"
className={
"btn btn--link " + (this.state.keysReadOnly ? "hidden" : "")
"btn btn-primary " + (this.state.keysReadOnly ? "hidden" : "")
}
onClick={this.generateAuth.bind(this)}
>
@@ -170,7 +167,7 @@ export class ChangePasswordModal extends React.Component {
<button
id="update-keys"
className={
"btn btn--link " + (this.state.keysReadOnly ? "hidden" : "")
"btn btn-success " + (this.state.keysReadOnly ? "hidden" : "")
}
onClick={this.setAuth.bind(this)}
>
@@ -178,7 +175,7 @@ export class ChangePasswordModal extends React.Component {
</button>
<button
id="cancel-change-password"
className="btn btn--link"
className="btn btn-link"
onClick={hideChangePassword}
>
Cancel

View File

@@ -33,23 +33,23 @@ let ConfirmModal = ({
bsSize="small"
animation={false}
show={show}
className={"dialog " + (baseClass || "")}
className={"modal-confirm " + (baseClass || "")}
>
<Modal.Body>
<div className="dialog__icon">
<ModalBody>
<div className="mc-icon">
<i className={icon} />
</div>
<div className="dialog__text">{text}</div>
<div className="dialog__sub">{sub}</div>
<div className="dialog__actions">
<button className="btn btn--link" onClick={okHandler}>
{okText}
</button>
<button className="btn btn--link" onClick={cancelHandler}>
{cancelText}
</button>
</div>
</Modal.Body>
<div className="mc-text">{text}</div>
<div className="mc-sub">{sub}</div>
</ModalBody>
<div className="modal-footer">
<button className="btn btn-danger" onClick={okHandler}>
{okText}
</button>
<button className="btn btn-link" onClick={cancelHandler}>
{cancelText}
</button>
</div>
</Modal>
)
}

View File

@@ -16,13 +16,26 @@
import React from "react"
import Path from "../objects/Path"
import Toolbar from "../objects/Toolbar"
import StorageInfo from "./StorageInfo"
import BrowserDropdown from "./BrowserDropdown"
import web from "../web"
import { minioBrowserPrefix } from "../constants"
export const Header = () => {
const loggedIn = web.LoggedIn()
return (
<header className="header">
<Toolbar />
<header className="fe-header">
<Path />
{loggedIn && <StorageInfo />}
<ul className="feh-actions">
{loggedIn ? (
<BrowserDropdown />
) : (
<a className="btn btn-danger" href={minioBrowserPrefix + "/login"}>
Login
</a>
)}
</ul>
</header>
)
}

View File

@@ -16,6 +16,11 @@
import React from "react"
export const Host = () => <small>{window.location.host}</small>
export const Host = () => (
<div className="fes-host">
<i className="fa fa-globe" />
<a href="/">{window.location.host}</a>
</div>
)
export default Host

View File

@@ -0,0 +1,70 @@
/*
* Minio Cloud Storage (C) 2016, 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
let InputGroup = ({
label,
id,
name,
value,
onChange,
type,
spellCheck,
required,
readonly,
autoComplete,
align,
className
}) => {
var input = (
<input
id={id}
name={name}
value={value}
onChange={onChange}
className="ig-text"
type={type}
spellCheck={spellCheck}
required={required}
autoComplete={autoComplete}
/>
)
if (readonly)
input = (
<input
id={id}
name={name}
value={value}
onChange={onChange}
className="ig-text"
type={type}
spellCheck={spellCheck}
required={required}
autoComplete={autoComplete}
disabled
/>
)
return (
<div className={"input-group " + align + " " + className}>
{input}
<i className="ig-helpers" />
<label className="ig-label">{label}</label>
</div>
)
}
export default InputGroup

View File

@@ -16,9 +16,11 @@
import React from "react"
import { connect } from "react-redux"
import classNames from "classnames"
import logo from "../../img/logo.svg"
import Alert from "../alert/Alert"
import * as actionsAlert from "../alert/actions"
import InputGroup from "./InputGroup"
import web from "../web"
import { Redirect } from "react-router-dom"
@@ -72,37 +74,39 @@ export class Login extends React.Component {
return (
<div className="login">
{alertBox}
<div className="login__inner">
<div className="login__header">
<img className="login__logo" src={logo} alt="" />
<div className="login__host">{window.location.host}</div>
</div>
<form className="login__form" onSubmit={this.handleSubmit.bind(this)}>
<div className="form-group">
<input
placeholder="Access Key"
type="text"
id="accessKey"
className="form-group__field"
spellCheck="false"
required="required"
/>
<i className="form-group__helper" />
</div>
<div className="form-group">
<input
placeholder="Secret Key"
type="password"
id="secretKey"
className="form-group__field"
spellCheck="false"
required="required"
/>
<i className="form-group__helper" />
</div>
<button className="login__btn" type="submit" />
<div className="l-wrap">
<form onSubmit={this.handleSubmit.bind(this)}>
<InputGroup
className="ig-dark"
label="Access Key"
id="accessKey"
name="username"
type="text"
spellCheck="false"
required="required"
autoComplete="username"
/>
<InputGroup
className="ig-dark"
label="Secret Key"
id="secretKey"
name="password"
type="password"
spellCheck="false"
required="required"
autoComplete="new-password"
/>
<button className="lw-btn" type="submit">
<i className="fa fa-sign-in" />
</button>
</form>
</div>
<div className="l-footer">
<a className="lf-logo" href="">
<img src={logo} alt="" />
</a>
<div className="lf-server">{window.location.host}</div>
</div>
</div>
)
}
@@ -111,12 +115,7 @@ export class Login extends React.Component {
const mapDispatchToProps = dispatch => {
return {
showAlert: (type, message) =>
dispatch(
actionsAlert.set({
type: type,
message: message
})
),
dispatch(actionsAlert.set({ type: type, message: message })),
clearAlert: () => dispatch(actionsAlert.clear())
}
}

View File

@@ -27,9 +27,9 @@ export const MainActions = ({
uploadFile,
showMakeBucketModal
}) => {
const uploadTooltip = <Tooltip id="tooltip-upload-file">Upload file</Tooltip>
const uploadTooltip = <Tooltip id="tt-upload-file">Upload file</Tooltip>
const makeBucketTooltip = (
<Tooltip id="tooltip-create-bucket">Create bucket</Tooltip>
<Tooltip id="tt-create-bucket">Create bucket</Tooltip>
)
const onFileUpload = e => {
e.preventDefault()
@@ -41,20 +41,15 @@ export const MainActions = ({
if (loggedIn || prefixWritable) {
return (
<Dropdown
dropup
pullRight
className="create"
id="main-actions"
componentClass="div"
>
<Dropdown.Toggle noCaret className="create__toggle" useAnchor={true}>
<i className="zmdi zmdi-plus" />
<i className="zmdi zmdi-close" />
<Dropdown dropup className="feb-actions" id="fe-action-toggle">
<Dropdown.Toggle noCaret className="feba-toggle">
<span>
<i className="fa fa-plus" />
</span>
</Dropdown.Toggle>
<Dropdown.Menu>
<OverlayTrigger placement="left" overlay={uploadTooltip}>
<a href="#" className="create__btn create__btn--upload">
<a href="#" className="feba-btn feba-upload">
<input
type="file"
onChange={onFileUpload}
@@ -62,7 +57,8 @@ export const MainActions = ({
id="file-input"
/>
<label htmlFor="file-input">
<i className="zmdi zmdi-upload" />
{" "}
<i className="fa fa-cloud-upload" />{" "}
</label>
</a>
</OverlayTrigger>
@@ -71,12 +67,14 @@ export const MainActions = ({
<a
href="#"
id="show-make-bucket"
className="create__btn create__btn--bucket"
className="feba-btn feba-bucket"
onClick={e => {
e.preventDefault()
showMakeBucketModal()
}}
/>
>
<i className="fa fa-hdd-o" />
</a>
</OverlayTrigger>
)}
</Dropdown.Menu>

View File

@@ -15,27 +15,29 @@
*/
import React from "react"
import MobileHeader from "./MobileHeader"
import Header from "./Header"
import SidebarBackdrop from "./SidebarBackdrop"
import ObjectsSection from "../objects/ObjectsSection"
import MainActions from "./MainActions"
import BucketPolicyModal from "../buckets/BucketPolicyModal"
import MakeBucketModal from "../buckets/MakeBucketModal"
import UploadModal from "../uploads/UploadModal"
import ObjectsBulkActions from "../objects/ObjectsBulkActions"
import Dropzone from "../uploads/Dropzone"
export const MainContent = ({ sidebarOpen }) => (
<section className="content">
<Header />
export const MainContent = () => (
<div className="fe-body">
<ObjectsBulkActions />
<MobileHeader />
<Dropzone>
<Header />
<ObjectsSection />
</Dropzone>
<MainActions />
<BucketPolicyModal />
<MakeBucketModal />
<UploadModal />
<SidebarBackdrop />
</section>
</div>
)
export default MainContent

View File

@@ -15,17 +15,34 @@
*/
import React from "react"
import classNames from "classnames"
import { connect } from "react-redux"
import logo from "../../img/logo.svg"
import * as actionsCommon from "./actions"
export const SidebarBackdrop = ({ sidebarOpen, toggleSidebar }) => (
<div
className="sidebar-backdrop"
onClick={e => {
e.stopPropagation()
toggleSidebar()
}}
/>
export const MobileHeader = ({ sidebarOpen, toggleSidebar }) => (
<header className="fe-header-mobile hidden-lg hidden-md">
<div
id="sidebar-toggle"
className={
"feh-trigger " +
classNames({
"feht-toggled": sidebarOpen
})
}
onClick={e => {
e.stopPropagation()
toggleSidebar()
}}
>
<div className="feht-lines">
<div className="top" />
<div className="center" />
<div className="bottom" />
</div>
</div>
<img className="mh-logo" src={logo} alt="" />
</header>
)
const mapStateToProps = state => {
@@ -40,4 +57,4 @@ const mapDispatchToProps = dispatch => {
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SidebarBackdrop)
export default connect(mapStateToProps, mapDispatchToProps)(MobileHeader)

View File

@@ -16,38 +16,37 @@
import React from "react"
import classNames from "classnames"
import ClickOutHandler from "react-onclickout"
import { connect } from "react-redux"
import logo from "../../img/logo.svg"
import Dropdown from "react-bootstrap/lib/Dropdown"
import BucketSearch from "../buckets/BucketSearch"
import BucketList from "../buckets/BucketList"
import StorageInfo from "./StorageInfo"
import Host from "./Host"
import * as actionsCommon from "./actions"
import web from "../web"
export const SideBar = ({ sidebarOpen }) => {
export const SideBar = ({ sidebarOpen, clickOutside }) => {
return (
<aside
className={classNames({
sidebar: true,
"sidebar--toggled": sidebarOpen
})}
>
<div className="sidebar__inner">
<div className="logo">
<img className="logo__img" src={logo} alt="" />
<div className="logo__title">
<h2>Minio Browser</h2>
<Host />
</div>
<ClickOutHandler onClickOut={clickOutside}>
<div
className={classNames({
"fe-sidebar": true,
toggled: sidebarOpen
})}
>
<div className="fes-header clearfix hidden-sm hidden-xs">
<img src={logo} alt="" />
<h2>Minio Browser</h2>
</div>
<div className="buckets">
<div className="fes-list">
{web.LoggedIn() && <BucketSearch />}
<BucketList />
</div>
<StorageInfo />
<Host />
</div>
</aside>
</ClickOutHandler>
)
}
@@ -59,7 +58,7 @@ const mapStateToProps = state => {
const mapDispatchToProps = dispatch => {
return {
toggleSidebar: () => dispatch(actionsCommon.toggleSidebar())
clickOutside: () => dispatch(actionsCommon.closeSidebar())
}
}

View File

@@ -1,43 +0,0 @@
/*
* Minio Cloud Storage (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from "react"
import { connect } from "react-redux"
import * as actionsCommon from "./actions"
export const SidebarToggle = ({ sidebarOpen, toggleSidebar }) => (
<button
className="toolbar__item zmdi zmdi-menu"
onClick={e => {
e.stopPropagation()
toggleSidebar()
}}
/>
)
const mapStateToProps = state => {
return {
sidebarOpen: state.browser.sidebarOpen
}
}
const mapDispatchToProps = dispatch => {
return {
toggleSidebar: () => dispatch(actionsCommon.toggleSidebar())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SidebarToggle)

View File

@@ -28,15 +28,22 @@ export class StorageInfo extends React.Component {
const { total, free } = this.props.storageInfo
const used = total - free
const usedPercent = used / total * 100 + "%"
const freePercent = free * 100 / total
return (
<div className="storage hidden-sm hidden-xs">
<small>
{humanize.filesize(total - free)} of {humanize.filesize(total)} Used
</small>
<div className="storage__progress">
<span style={{ width: usedPercent }} />
<div className="feh-usage">
<div className="fehu-chart">
<div style={{ width: usedPercent }} />
</div>
<ul>
<li>
<span>Used: </span>
{humanize.filesize(total - free)}
</li>
<li className="pull-right">
<span>Free: </span>
{humanize.filesize(total - used)}
</li>
</ul>
</div>
)
}

View File

@@ -35,7 +35,7 @@ describe("AboutModal", () => {
const wrapper = shallow(
<AboutModal serverInfo={serverInfo} hideAbout={hideAbout} />
)
wrapper.find("i").simulate("click")
wrapper.find("button").simulate("click")
expect(hideAbout).toHaveBeenCalled()
})
})

View File

@@ -24,6 +24,6 @@ const mockStore = configureStore()
describe("Browser", () => {
it("should render without crashing", () => {
const store = mockStore()
shallow(<Browser store={store} />)
shallow(<Browser store={store}/>)
})
})

View File

@@ -47,9 +47,7 @@ describe("BrowserDropdown", () => {
const wrapper = shallow(
<BrowserDropdown serverInfo={serverInfo} fetchServerInfo={jest.fn()} />
)
wrapper.find("#show-about").simulate("click", {
preventDefault: jest.fn()
})
wrapper.find("#show-about").simulate("click", { preventDefault: jest.fn() })
wrapper.update()
expect(wrapper.state("showAboutModal")).toBeTruthy()
expect(wrapper.find("AboutModal").length).toBe(1)
@@ -59,9 +57,7 @@ describe("BrowserDropdown", () => {
const wrapper = shallow(
<BrowserDropdown serverInfo={serverInfo} fetchServerInfo={jest.fn()} />
)
wrapper.find("#logout").simulate("click", {
preventDefault: jest.fn()
})
wrapper.find("#logout").simulate("click", { preventDefault: jest.fn() })
expect(window.location.pathname.endsWith("/login")).toBeTruthy()
})
})

View File

@@ -20,24 +20,16 @@ import { ChangePasswordModal } from "../ChangePasswordModal"
jest.mock("../../web", () => ({
GetAuth: jest.fn(() => {
return Promise.resolve({
accessKey: "test1",
secretKey: "test2"
})
return Promise.resolve({ accessKey: "test1", secretKey: "test2" })
}),
GenerateAuth: jest.fn(() => {
return Promise.resolve({
accessKey: "gen1",
secretKey: "gen2"
})
return Promise.resolve({ accessKey: "gen1", secretKey: "gen2" })
}),
SetAuth: jest.fn(({ accessKey, secretKey }) => {
if (accessKey == "test3" && secretKey == "test4") {
return Promise.resolve({})
} else {
return Promise.reject({
message: "Error"
})
return Promise.reject({ message: "Error" })
}
})
}))
@@ -48,9 +40,7 @@ describe("ChangePasswordModal", () => {
memory: "test",
platform: "test",
runtime: "test",
info: {
isEnvCreds: false
}
info: { isEnvCreds: false }
}
it("should render without crashing", () => {
@@ -66,12 +56,7 @@ describe("ChangePasswordModal", () => {
})
it("should show readonly keys when isEnvCreds is true", () => {
const newServerInfo = {
...serverInfo,
info: {
isEnvCreds: true
}
}
const newServerInfo = { ...serverInfo, info: { isEnvCreds: true } }
const wrapper = shallow(<ChangePasswordModal serverInfo={newServerInfo} />)
expect(wrapper.state("accessKey")).toBe("xxxxxxxxx")
expect(wrapper.state("secretKey")).toBe("xxxxxxxxx")
@@ -95,16 +80,12 @@ describe("ChangePasswordModal", () => {
const wrapper = shallow(
<ChangePasswordModal serverInfo={serverInfo} showAlert={showAlert} />
)
wrapper.find("#accessKey").simulate("change", {
target: {
value: "test3"
}
})
wrapper.find("#secretKey").simulate("change", {
target: {
value: "test4"
}
})
wrapper
.find("#accessKey")
.simulate("change", { target: { value: "test3" } })
wrapper
.find("#secretKey")
.simulate("change", { target: { value: "test4" } })
wrapper.find("#update-keys").simulate("click")
setImmediate(() => {
expect(showAlert).toHaveBeenCalledWith({

View File

@@ -18,8 +18,25 @@ import React from "react"
import { shallow } from "enzyme"
import Header from "../Header"
jest.mock("../../web", () => ({
LoggedIn: jest
.fn(() => true)
.mockReturnValueOnce(true)
.mockReturnValueOnce(false)
}))
describe("Header", () => {
it("should render without crashing", () => {
shallow(<Header />)
})
it("should render Login button when the user has not LoggedIn", () => {
const wrapper = shallow(<Header />)
expect(wrapper.find("a").text()).toBe("Login")
})
it("should render StorageInfo and BrowserDropdown when the user has LoggedIn", () => {
const wrapper = shallow(<Header />)
expect(wrapper.find("Connect(BrowserDropdown)").length).toBe(1)
expect(wrapper.find("Connect(StorageInfo)").length).toBe(1)
})
})

View File

@@ -19,12 +19,9 @@ import { shallow, mount } from "enzyme"
import { Login } from "../Login"
import web from "../../web"
jest.mock("../../web", () => ({
jest.mock('../../web', () => ({
Login: jest.fn(() => {
return Promise.resolve({
token: "test",
uiVersion: "2018-02-01T01:17:47Z"
})
return Promise.resolve({ token: "test", uiVersion: "2018-02-01T01:17:47Z" })
}),
LoggedIn: jest.fn()
}))
@@ -33,87 +30,70 @@ describe("Login", () => {
const dispatchMock = jest.fn()
const showAlertMock = jest.fn()
const clearAlertMock = jest.fn()
it("should render without crashing", () => {
shallow(
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger" }}
showAlert={showAlertMock}
clearAlert={clearAlertMock}
/>
)
shallow(<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger"}}
showAlert={showAlertMock}
clearAlert={clearAlertMock}
/>)
})
it("should initially have the is-guest class", () => {
const wrapper = shallow(
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger" }}
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger"}}
showAlert={showAlertMock}
clearAlert={clearAlertMock}
/>,
{
attachTo: document.body
}
{ attachTo: document.body }
)
expect(document.body.classList.contains("is-guest")).toBeTruthy()
})
it("should throw an alert if the keys are empty in login form", () => {
const wrapper = mount(
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger" }}
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger"}}
showAlert={showAlertMock}
clearAlert={clearAlertMock}
/>,
{
attachTo: document.body
}
{ attachTo: document.body }
)
// case where both keys are empty - displays the second warning
wrapper.find("form").simulate("submit")
expect(showAlertMock).toHaveBeenCalledWith(
"danger",
"Secret Key cannot be empty"
)
expect(showAlertMock).toHaveBeenCalledWith("danger", "Secret Key cannot be empty")
// case where access key is empty
document.getElementById("secretKey").value = "secretKey"
wrapper.find("form").simulate("submit")
expect(showAlertMock).toHaveBeenCalledWith(
"danger",
"Access Key cannot be empty"
)
expect(showAlertMock).toHaveBeenCalledWith("danger", "Access Key cannot be empty")
// case where secret key is empty
document.getElementById("accessKey").value = "accessKey"
wrapper.find("form").simulate("submit")
expect(showAlertMock).toHaveBeenCalledWith(
"danger",
"Secret Key cannot be empty"
)
expect(showAlertMock).toHaveBeenCalledWith("danger", "Secret Key cannot be empty")
})
it("should call web.Login with correct arguments if both keys are entered", () => {
const wrapper = mount(
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger" }}
<Login
dispatch={dispatchMock}
alert={{ show: false, type: "danger"}}
showAlert={showAlertMock}
clearAlert={clearAlertMock}
/>,
{
attachTo: document.body
}
{ attachTo: document.body }
)
document.getElementById("accessKey").value = "accessKey"
document.getElementById("secretKey").value = "secretKey"
wrapper.find("form").simulate("submit")
expect(web.Login).toHaveBeenCalledWith({
username: "accessKey",
password: "secretKey"
"username": "accessKey",
"password": "secretKey"
})
})
})

View File

@@ -54,23 +54,19 @@ describe("MainActions", () => {
const wrapper = shallow(
<MainActions showMakeBucketModal={showMakeBucketModal} />
)
wrapper.find("#show-make-bucket").simulate("click", {
preventDefault: jest.fn()
})
wrapper
.find("#show-make-bucket")
.simulate("click", { preventDefault: jest.fn() })
expect(showMakeBucketModal).toHaveBeenCalled()
})
it("should call uploadFile when a file is selected for upload", () => {
const uploadFile = jest.fn()
const wrapper = shallow(<MainActions uploadFile={uploadFile} />)
const file = new Blob(["file content"], {
type: "text/plain"
})
const file = new Blob(["file content"], { type: "text/plain" })
wrapper.find("#file-input").simulate("change", {
preventDefault: jest.fn(),
target: {
files: [file]
}
target: { files: [file] }
})
expect(uploadFile).toHaveBeenCalledWith(file)
})

View File

@@ -16,21 +16,21 @@
import React from "react"
import { shallow } from "enzyme"
import { SidebarToggle } from "../SidebarToggle"
import { MobileHeader } from "../MobileHeader"
describe("SidebarToggle", () => {
describe("Bucket", () => {
it("should render without crashing", () => {
shallow(<SidebarToggle sidebarOpen={false} />)
shallow(<MobileHeader sidebarOpen={false} />)
})
it("should toggleSidebar when trigger is clicked", () => {
const toggleSidebar = jest.fn()
const wrapper = shallow(
<SidebarToggle sidebarOpen={false} toggleSidebar={toggleSidebar} />
<MobileHeader sidebarOpen={false} toggleSidebar={toggleSidebar} />
)
wrapper.find("button").simulate("click", {
stopPropagation: jest.fn()
})
wrapper
.find("#sidebar-toggle")
.simulate("click", { stopPropagation: jest.fn() })
expect(toggleSidebar).toHaveBeenCalled()
})
})

View File

@@ -31,4 +31,11 @@ describe("SideBar", () => {
const wrapper = shallow(<SideBar />)
expect(wrapper.find("Connect(BucketSearch)").length).toBe(0)
})
it("should call clickOutside when the user clicks outside the sidebar", () => {
const clickOutside = jest.fn()
const wrapper = shallow(<SideBar clickOutside={clickOutside} />)
wrapper.simulate("clickOut", { preventDefault: jest.fn() })
expect(clickOutside).toHaveBeenCalled()
})
})

View File

@@ -20,12 +20,7 @@ import * as actionsCommon from "../actions"
jest.mock("../../web", () => ({
StorageInfo: jest.fn(() => {
return Promise.resolve({
storageInfo: {
Total: 100,
Free: 60
}
})
return Promise.resolve({ storageInfo: { Total: 100, Free: 60 } })
}),
ServerInfo: jest.fn(() => {
return Promise.resolve({
@@ -45,13 +40,7 @@ describe("Common actions", () => {
it("creates common/SET_STORAGE_INFO after fetching the storage details ", () => {
const store = mockStore()
const expectedActions = [
{
type: "common/SET_STORAGE_INFO",
storageInfo: {
total: 100,
free: 60
}
}
{ type: "common/SET_STORAGE_INFO", storageInfo: { total: 100, free: 60 } }
]
return store.dispatch(actionsCommon.fetchStorageInfo()).then(() => {
const actions = store.getActions()

View File

@@ -32,9 +32,7 @@ describe("common reducer", () => {
it("should handle TOGGLE_SIDEBAR", () => {
expect(
reducer(
{
sidebarOpen: false
},
{ sidebarOpen: false },
{
type: actionsCommon.TOGGLE_SIDEBAR
}
@@ -47,9 +45,7 @@ describe("common reducer", () => {
it("should handle CLOSE_SIDEBAR", () => {
expect(
reducer(
{
sidebarOpen: true
},
{ sidebarOpen: true },
{
type: actionsCommon.CLOSE_SIDEBAR
}
@@ -65,17 +61,11 @@ describe("common reducer", () => {
{},
{
type: actionsCommon.SET_STORAGE_INFO,
storageInfo: {
total: 100,
free: 40
}
storageInfo: { total: 100, free: 40 }
}
)
).toEqual({
storageInfo: {
total: 100,
free: 40
}
storageInfo: { total: 100, free: 40 }
})
})

View File

@@ -19,10 +19,7 @@ import * as actionsCommon from "./actions"
export default (
state = {
sidebarOpen: false,
storageInfo: {
total: 0,
free: 0
},
storageInfo: { total: 0, free: 0 },
serverInfo: {}
},
action
@@ -41,10 +38,7 @@ export default (
storageInfo: action.storageInfo
})
case actionsCommon.SET_SERVER_INFO:
return {
...state,
serverInfo: action.serverInfo
}
return { ...state, serverInfo: action.serverInfo }
default:
return state
}