Skip to content

Commit bc10a8d

Browse files
author
Zsolt Meszaros
committed
Merge branch 'master' into feature/toast
2 parents d398435 + 9aa99f8 commit bc10a8d

File tree

9 files changed

+230
-41
lines changed

9 files changed

+230
-41
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
## User Stories
66

77
- [x] As a user I can see a randomly generated password on page load
8-
- [ ] As a user I can change the length of the generated password
8+
- [x] As a user I can change the length of the generated password
99
- [x] As a user I can include/exclude uppercase letters, lowercase letters, numbers, and symbols
1010
- [x] As a user I can see a new password whenever I make a change in my settings
1111
- [x] As a user I can click on a button to copy the password to clipboard without selecting it first

cypress/e2e/Options.spec.js

+41-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,27 @@
1+
/**
2+
* Helper function to change value of a slider. It sets the value of the DOM
3+
* node and fires a change event on it.
4+
*
5+
* We need this because `onChange` is not triggered with React using a simple
6+
* `.invoke('val', value).trigger('change');`
7+
*
8+
* https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210
9+
*/
10+
function setValueAndFireChange(input, value) {
11+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
12+
window.HTMLInputElement.prototype,
13+
'value',
14+
).set;
15+
16+
nativeInputValueSetter.call(input, value);
17+
input.dispatchEvent(
18+
new Event('change', {
19+
value,
20+
bubbles: true,
21+
}),
22+
);
23+
}
24+
125
describe('Password customisation', () => {
226
beforeEach(() => {
327
cy.visit('/');
@@ -132,19 +156,22 @@ describe('Password customisation', () => {
132156
.should('have.length', 0);
133157
});
134158

135-
it('changes password length if length option is changed', () => {
136-
cy.get('label:last-child()').click();
137-
cy.focused()
138-
.type('{rightarrow}')
139-
.type('{backspace}');
159+
it('changes password length if slider is changed', () => {
160+
const newLength = 5;
140161

141-
cy.get('[name="generatedPassword"]')
162+
cy.get('input[type="range"]').then(([rangeInput]) => {
163+
setValueAndFireChange(rangeInput, newLength);
164+
});
165+
166+
cy.get('[name ="generatedPassword"]')
142167
.invoke('val')
143-
.should('have.length', 4);
168+
.should('have.length', newLength);
144169
});
145170

146171
it('saves settings to localStorage on change', () => {
147172
const key = 'settings';
173+
const newLength = 5;
174+
148175
expect(localStorage.getItem('settings')).to.eq(null);
149176

150177
cy.get('label')
@@ -155,12 +182,12 @@ describe('Password customisation', () => {
155182
.next()
156183
.click();
157184

158-
cy.get('label:last-child()').click();
159-
cy.focused()
160-
.type('{rightarrow}')
161-
.type('{backspace}');
185+
cy.get('input[type="range"]').then(([rangeInput]) => {
186+
setValueAndFireChange(rangeInput, newLength);
187+
});
162188

163189
cy.log(localStorage.getItem(key));
190+
164191
cy.window().then(win => {
165192
const actual = win.localStorage.getItem(key);
166193
expect(actual).to.eq(
@@ -169,7 +196,7 @@ describe('Password customisation', () => {
169196
hasNumbers: true,
170197
hasSymbols: false,
171198
hasUppercase: false,
172-
length: 4,
199+
length: newLength,
173200
}),
174201
);
175202
});
@@ -178,7 +205,7 @@ describe('Password customisation', () => {
178205

179206
cy.get('[name="generatedPassword"]')
180207
.invoke('val')
181-
.should('have.length', 4);
208+
.should('have.length', newLength);
182209

183210
cy.get('input[type="checkbox"]')
184211
.eq(0)
@@ -196,6 +223,6 @@ describe('Password customisation', () => {
196223
.eq(3)
197224
.should('not.be.checked');
198225

199-
cy.get('label:last-child() input').should('have.value', '4');
226+
cy.get('input[type="range"]').should('have.value', newLength.toString());
200227
});
201228
});

package-lock.json

+17-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"styled-components": "^4.4.0"
1111
},
1212
"devDependencies": {
13-
"cypress": "^3.4.1",
13+
"cypress": "^3.6.1",
1414
"enzyme": "^3.10.0",
1515
"enzyme-adapter-react-16": "^1.14.0",
1616
"enzyme-to-json": "^3.4.2",

src/components/App.js

+22-19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from 'styled-components/macro';
33

44
import PasswordInput from './PasswordInput';
55
import PasswordStrength from './PasswordStrength';
6+
import Range from './Range';
67
import SwitchToggle from './SwitchToggle';
78
import { Wrapper } from './GlobalStyles';
89

@@ -47,15 +48,19 @@ function App() {
4748
const [hasLowercase, setHasLowercase] = useState(settings.hasLowercase);
4849
const [hasUppercase, setHasUppercase] = useState(settings.hasUppercase);
4950
const [length, setLength] = useState(settings.length);
50-
const [password, setPassword] = useState(() =>
51-
generatePassword({
52-
hasLowercase,
53-
hasNumbers,
54-
hasSymbols,
55-
hasUppercase,
56-
length,
57-
}),
58-
);
51+
const [password, setPassword] = useState(() => {
52+
try {
53+
return generatePassword({
54+
hasLowercase,
55+
hasNumbers,
56+
hasSymbols,
57+
hasUppercase,
58+
length,
59+
});
60+
} catch (e) {
61+
return ' ';
62+
}
63+
});
5964
const [strengthScore, setStrengthScore] = useState(
6065
calculatePasswordStrength(password),
6166
);
@@ -132,16 +137,14 @@ function App() {
132137
isChecked={hasSymbols}
133138
onToggle={setHasSymbols}
134139
/>
135-
<label>
136-
<input
137-
type="number"
138-
min="4"
139-
max="99"
140-
value={length}
141-
onChange={e => setLength(+e.target.value)}
142-
/>
143-
Length
144-
</label>
140+
<Range
141+
aria-label="Password length"
142+
id="length"
143+
min={4}
144+
max={99}
145+
onChange={setLength}
146+
value={length}
147+
/>
145148
</Options>
146149
</Wrapper>
147150
</Main>

src/components/GlobalStyles.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const Wrapper = styled.div`
1818
`;
1919

2020
export default createGlobalStyle`
21-
*, *::after, *::before {
21+
*, *::after, *::before {
2222
box-sizing: inherit;
2323
}
2424
@@ -41,7 +41,7 @@ export default createGlobalStyle`
4141
font-weight: 600;
4242
}
4343
44-
button, input {
44+
button, input, output {
4545
border: 1px solid ${({ theme }) => theme.border};
4646
border-radius: ${({ theme }) => theme.borderRadius};
4747
font-family: inherit;

src/components/PasswordInput.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ const InputWrapper = styled.div`
1414
1515
&::after {
1616
background-image: ${({ theme }) =>
17-
`linear-gradient(to right, transparent, ${theme.input})`};
17+
`linear-gradient(to right, rgba(255, 255, 255, 0), ${theme.input})`};
1818
content: '';
1919
height: 4rem;
20+
pointer-events: none;
2021
position: absolute;
2122
right: 1rem;
2223
top: 1px;

0 commit comments

Comments
 (0)