diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-08-01 14:21:58 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-08-08 15:26:34 +0200 |
| commit | 2f6e76531419c0653212410dc992bf952ee5fbe5 (patch) | |
| tree | 43e2eae17b1ae61da6a05a2e333b88cc6060b2ee | |
| parent | 21c62239a0ef590e77f3a5a3448f36259755d6d8 (diff) | |
| download | mullvadvpn-2f6e76531419c0653212410dc992bf952ee5fbe5.tar.xz mullvadvpn-2f6e76531419c0653212410dc992bf952ee5fbe5.zip | |
Pull account expiry on demand
| -rw-r--r-- | app/components/Account.js | 6 | ||||
| -rw-r--r-- | app/components/Connect.js | 13 | ||||
| -rw-r--r-- | app/components/Settings.js | 32 | ||||
| -rw-r--r-- | app/containers/ConnectPage.js | 1 | ||||
| -rw-r--r-- | app/containers/SettingsPage.js | 9 | ||||
| -rw-r--r-- | test/components/Account.spec.js | 3 | ||||
| -rw-r--r-- | test/components/Connect.spec.js | 3 | ||||
| -rw-r--r-- | test/components/Settings.spec.js | 129 |
8 files changed, 105 insertions, 91 deletions
diff --git a/app/components/Account.js b/app/components/Account.js index 2edf277fa4..dd4994c76c 100644 --- a/app/components/Account.js +++ b/app/components/Account.js @@ -12,7 +12,7 @@ import { formatAccount } from '../lib/formatters'; import type { AccountToken } from '../lib/daemon-rpc'; -export type AccountProps = { +type Props = { accountToken: AccountToken, accountExpiry: string, expiryLocale: string, @@ -28,7 +28,7 @@ type State = { showAccountTokenCopiedMessage: boolean, }; -export default class Account extends Component<AccountProps, State> { +export default class Account extends Component<Props, State> { state = { isRefreshingExpiry: false, showAccountTokenCopiedMessage: false, @@ -119,7 +119,7 @@ export default class Account extends Component<AccountProps, State> { <Text style={styles.account__row_label}>Paid until</Text> {isOutOfTime ? ( <Text style={styles.account__out_of_time} testName="account__out_of_time"> - OUT OF TIME + {'OUT OF TIME'} </Text> ) : ( <Text style={styles.account__row_value}>{formattedExpiry}</Text> diff --git a/app/components/Connect.js b/app/components/Connect.js index 037ab02495..cd27b33719 100644 --- a/app/components/Connect.js +++ b/app/components/Connect.js @@ -15,7 +15,7 @@ import Map from './Map'; import type { HeaderBarStyle } from './HeaderBar'; import type { ConnectionReduxState } from '../redux/connection/reducers'; -export type ConnectProps = { +type Props = { connection: ConnectionReduxState, accountExpiry: string, selectedRelayName: string, @@ -25,14 +25,15 @@ export type ConnectProps = { onCopyIP: () => void, onDisconnect: () => void, onExternalLink: (type: string) => void, + updateAccountExpiry: () => Promise<void>, }; -type ConnectState = { +type State = { showCopyIPMessage: boolean, mapOffset: [number, number], }; -export default class Connect extends Component<ConnectProps, ConnectState> { +export default class Connect extends Component<Props, State> { state = { showCopyIPMessage: false, mapOffset: [0, 0], @@ -40,7 +41,7 @@ export default class Connect extends Component<ConnectProps, ConnectState> { _copyTimer: ?TimeoutID; - shouldComponentUpdate(nextProps: ConnectProps, nextState: ConnectState) { + shouldComponentUpdate(nextProps: Props, nextState: State) { const { connection: prevConnection, ...otherPrevProps } = this.props; const { connection: nextConnection, ...otherNextProps } = nextProps; @@ -56,6 +57,10 @@ export default class Connect extends Component<ConnectProps, ConnectState> { ); } + componentDidMount() { + this.props.updateAccountExpiry(); + } + componentWillUnmount() { if (this._copyTimer) { clearTimeout(this._copyTimer); diff --git a/app/components/Settings.js b/app/components/Settings.js index 4a8784fe0e..c447940338 100644 --- a/app/components/Settings.js +++ b/app/components/Settings.js @@ -11,13 +11,12 @@ import CustomScrollbars from './CustomScrollbars'; import styles from './SettingsStyles'; import Img from './Img'; -import type { AccountReduxState } from '../redux/account/reducers'; -import type { SettingsReduxState } from '../redux/settings/reducers'; +import type { LoginState } from '../redux/account/reducers'; -export type SettingsProps = { - account: AccountReduxState, - settings: SettingsReduxState, - version: string, +type Props = { + loginState: LoginState, + accountExpiry: ?string, + appVersion: string, onQuit: () => void, onClose: () => void, onViewAccount: () => void, @@ -25,9 +24,14 @@ export type SettingsProps = { onViewPreferences: () => void, onViewAdvancedSettings: () => void, onExternalLink: (type: string) => void, + updateAccountExpiry: () => Promise<void>, }; -export default class Settings extends Component<SettingsProps> { +export default class Settings extends Component<Props> { + componentDidMount() { + this.props.updateAccountExpiry(); + } + render() { return ( <Layout> @@ -60,17 +64,17 @@ export default class Settings extends Component<SettingsProps> { } _renderTopButtons() { - const isLoggedIn = this.props.account.status === 'ok'; + const isLoggedIn = this.props.loginState === 'ok'; if (!isLoggedIn) { return null; } - let isOutOfTime = false, - formattedExpiry = ''; - const expiryIso = this.props.account.expiry; + let isOutOfTime = false; + let formattedExpiry = ''; + const expiryIso = this.props.accountExpiry; if (isLoggedIn && expiryIso) { - const expiry = moment(this.props.account.expiry); + const expiry = moment(expiryIso); isOutOfTime = expiry.isSameOrBefore(moment()); formattedExpiry = (expiry.fromNow(true) + ' left').toUpperCase(); } @@ -86,7 +90,7 @@ export default class Settings extends Component<SettingsProps> { <Cell.SubText testName="settings__account_paid_until_subtext" style={styles.settings__account_paid_until_Label__error}> - OUT OF TIME + {'OUT OF TIME'} </Cell.SubText> <Img height={12} width={7} source="icon-chevron" /> </Cell.CellButton> @@ -136,7 +140,7 @@ export default class Settings extends Component<SettingsProps> { // the version in package.json has to be semver, but we use a YEAR.release-channel // version scheme. in package.json we thus have to write YEAR.release.X-channel and // this function is responsible for removing .X part. - return this.props.version + return this.props.appVersion .replace('.0-', '-') // remove the .0 in 2018.1.0-beta9 .replace(/\.0$/, ''); // remove the .0 in 2018.1.0 } diff --git a/app/containers/ConnectPage.js b/app/containers/ConnectPage.js index 6f906f1c82..d1d11af334 100644 --- a/app/containers/ConnectPage.js +++ b/app/containers/ConnectPage.js @@ -83,6 +83,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) => } }, onExternalLink: (type) => openLink(links[type]), + updateAccountExpiry: () => props.app.updateAccountExpiry(), }; }; diff --git a/app/containers/SettingsPage.js b/app/containers/SettingsPage.js index 8ffd0691fd..f0e8d7c53b 100644 --- a/app/containers/SettingsPage.js +++ b/app/containers/SettingsPage.js @@ -11,11 +11,11 @@ import type { ReduxState, ReduxDispatch } from '../redux/store'; import type { SharedRouteProps } from '../routes'; const mapStateToProps = (state: ReduxState) => ({ - account: state.account, - settings: state.settings, - version: getAppVersion(), + loginState: state.account.status, + accountExpiry: state.account.expiry, + appVersion: getAppVersion(), }); -const mapDispatchToProps = (dispatch: ReduxDispatch, _props: SharedRouteProps) => { +const mapDispatchToProps = (dispatch: ReduxDispatch, props: SharedRouteProps) => { const { push: pushHistory } = bindActionCreators({ push }, dispatch); return { onQuit: () => exit(), @@ -25,6 +25,7 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, _props: SharedRouteProps) = onViewPreferences: () => pushHistory('/settings/preferences'), onViewAdvancedSettings: () => pushHistory('/settings/advanced'), onExternalLink: (type) => openLink(links[type]), + updateAccountExpiry: () => props.app.updateAccountExpiry(), }; }; diff --git a/test/components/Account.spec.js b/test/components/Account.spec.js index a850c6af06..703ce3d27f 100644 --- a/test/components/Account.spec.js +++ b/test/components/Account.spec.js @@ -4,7 +4,8 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import Account from '../../app/components/Account'; import { BackBarItem } from '../../app/components/NavigationBar'; -import type { AccountProps } from '../../app/components/Account'; + +type AccountProps = React.ElementProps<typeof Account>; describe('components/Account', () => { const makeProps = (mergeProps: $Shape<AccountProps>): AccountProps => { diff --git a/test/components/Connect.spec.js b/test/components/Connect.spec.js index d21a8eb510..67dae303d8 100644 --- a/test/components/Connect.spec.js +++ b/test/components/Connect.spec.js @@ -5,7 +5,7 @@ import { shallow } from 'enzyme'; import Connect from '../../app/components/Connect'; -import type { ConnectProps } from '../../app/components/Connect'; +type ConnectProps = React.ElementProps<typeof Connect>; describe('components/Connect', () => { it('shows unsecured hints when disconnected', () => { @@ -125,6 +125,7 @@ const defaultProps: ConnectProps = { country: null, city: null, }, + updateAccountExpiry: () => Promise.resolve(), }; function renderWithProps(customProps: $Shape<ConnectProps>) { diff --git a/test/components/Settings.spec.js b/test/components/Settings.spec.js index 9edf46fcc3..4d52d96b11 100644 --- a/test/components/Settings.spec.js +++ b/test/components/Settings.spec.js @@ -5,57 +5,14 @@ import { shallow } from 'enzyme'; import Settings from '../../app/components/Settings'; import { CloseBarItem } from '../../app/components/NavigationBar'; -import type { AccountReduxState } from '../../app/redux/account/reducers'; -import type { SettingsReduxState } from '../../app/redux/settings/reducers'; -import type { SettingsProps } from '../../app/components/Settings'; +type SettingsProps = React.ElementProps<typeof Settings>; describe('components/Settings', () => { - const loggedOutAccountState: AccountReduxState = { - accountToken: null, - accountHistory: [], - expiry: null, - status: 'none', - error: null, - }; - - const loggedInAccountState: AccountReduxState = { - accountToken: '1234', - accountHistory: [], - expiry: new Date('2038-01-01').toISOString(), - status: 'ok', - error: null, - }; - - const unpaidAccountState: AccountReduxState = { - accountToken: '1234', - accountHistory: [], - expiry: new Date('2001-01-01').toISOString(), - status: 'ok', - error: null, - }; - - const settingsState: SettingsReduxState = { - relaySettings: { - normal: { - location: 'any', - protocol: 'udp', - port: 1301, - }, - }, - relayLocations: [], - autoConnect: false, - allowLan: false, - }; - - const makeProps = ( - anAccountState: AccountReduxState, - aSettingsState: SettingsReduxState, - mergeProps: $Shape<SettingsProps> = {}, - ): SettingsProps => { + const makeProps = (mergeProps: $Shape<SettingsProps> = {}): SettingsProps => { const defaultProps: SettingsProps = { - account: anAccountState, - settings: aSettingsState, - version: '', + loginState: 'none', + accountExpiry: null, + appVersion: '', onQuit: () => {}, onClose: () => {}, onViewAccount: () => {}, @@ -63,67 +20,97 @@ describe('components/Settings', () => { onViewAdvancedSettings: () => {}, onViewPreferences: () => {}, onExternalLink: (_type) => {}, + updateAccountExpiry: () => Promise.resolve(), }; return Object.assign({}, defaultProps, mergeProps); }; it('should show quit button when logged out', () => { - const props = makeProps(loggedOutAccountState, settingsState); + const props = makeProps({ + loginState: 'none', + accountExpiry: null, + }); const component = getComponent(render(props), 'settings__quit'); expect(component).to.have.length(1); }); it('should show quit button when logged in', () => { - const props = makeProps(loggedInAccountState, settingsState); + const props = makeProps({ + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', + }); const component = getComponent(render(props), 'settings__quit'); expect(component).to.have.length(1); }); it('should show external links when logged out', () => { - const props = makeProps(loggedOutAccountState, settingsState); + const props = makeProps({ + loginState: 'none', + accountExpiry: null, + }); const component = getComponent(render(props), 'settings__external_link'); expect(component.length).to.be.above(0); }); it('should show external links when logged in', () => { - const props = makeProps(loggedInAccountState, settingsState); + const props = makeProps({ + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', + }); const component = getComponent(render(props), 'settings__external_link'); expect(component.length).to.be.above(0); }); it('should show account section when logged in', () => { - const props = makeProps(loggedInAccountState, settingsState); + const props = makeProps({ + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', + }); const component = getComponent(render(props), 'settings__account'); expect(component).to.have.length(1); }); it('should hide account section when logged out', () => { - const props = makeProps(loggedOutAccountState, settingsState); + const props = makeProps({ + loginState: 'none', + accountExpiry: null, + }); const elements = getComponent(render(props), 'settings__account'); expect(elements).to.have.length(0); }); it('should hide account link when not logged in', () => { - const props = makeProps(loggedOutAccountState, settingsState); + const props = makeProps({ + loginState: 'none', + accountExpiry: null, + }); const elements = getComponent(render(props), 'settings__view_account'); expect(elements).to.have.length(0); }); it('should show out-of-time message for unpaid account', () => { - const props = makeProps(unpaidAccountState, settingsState); + const props = makeProps({ + accountExpiry: new Date('2001-01-01').toISOString(), + loginState: 'ok', + }); const component = getComponent(render(props), 'settings__account_paid_until_subtext'); expect(component.children().text()).to.equal('OUT OF TIME'); }); it('should hide out-of-time message for paid account', () => { - const props = makeProps(loggedInAccountState, settingsState); + const props = makeProps({ + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', + }); const component = getComponent(render(props), 'settings__account_paid_until_subtext'); expect(component.children().text()).not.to.equal('OUT OF TIME'); }); it('should call close callback', (done) => { - const props = makeProps(loggedOutAccountState, settingsState, { + const props = makeProps({ onClose: () => done(), + loginState: 'none', + accountExpiry: null, }); const component = render(props) .find(CloseBarItem) @@ -132,40 +119,51 @@ describe('components/Settings', () => { }); it('should call quit callback', (done) => { - const props = makeProps(loggedOutAccountState, settingsState, { + const props = makeProps({ onQuit: () => done(), + + loginState: 'none', + accountExpiry: null, }); const component = getComponent(render(props), 'settings__quit'); component.simulate('press'); }); it('should call account callback', (done) => { - const props = makeProps(loggedInAccountState, settingsState, { + const props = makeProps({ onViewAccount: () => done(), + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', }); const component = getComponent(render(props), 'settings__account_paid_until_button'); component.simulate('press'); }); it('should call advanced settings callback', (done) => { - const props = makeProps(loggedInAccountState, settingsState, { + const props = makeProps({ onViewAdvancedSettings: () => done(), + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', }); const component = getComponent(render(props), 'settings__advanced'); component.simulate('press'); }); it('should call preferences callback', (done) => { - const props = makeProps(loggedInAccountState, settingsState, { + const props = makeProps({ onViewPreferences: () => done(), + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', }); const component = getComponent(render(props), 'settings__preferences'); component.simulate('press'); }); it('should call support callback', (done) => { - const props = makeProps(loggedInAccountState, settingsState, { + const props = makeProps({ onViewSupport: () => done(), + accountExpiry: new Date('2038-01-01').toISOString(), + loginState: 'ok', }); const component = getComponent(render(props), 'settings__view_support'); component.simulate('press'); @@ -173,10 +171,13 @@ describe('components/Settings', () => { it('should call external links callback', () => { const collectedExternalLinkTypes: Array<string> = []; - const props = makeProps(loggedOutAccountState, settingsState, { + const props = makeProps({ onExternalLink: (type) => { collectedExternalLinkTypes.push(type); }, + + loginState: 'none', + accountExpiry: null, }); const container = getComponent(render(props), 'settings__external_link'); container |
