Upload 5.23
This commit is contained in:
@@ -14,7 +14,8 @@ mongoose.set('strictQuery', false)
|
||||
|
||||
logger.info('connecting to', config.MONGODB_URI)
|
||||
|
||||
mongoose.connect(config.MONGODB_URI)
|
||||
mongoose
|
||||
.connect(config.MONGODB_URI)
|
||||
.then(() => {
|
||||
logger.info('connected to MongoDB')
|
||||
})
|
||||
@@ -33,6 +34,11 @@ app.use('/api/blogs', blogsRouter)
|
||||
app.use('/api/users', usersRouter)
|
||||
app.use('/api/login', loginRouter)
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
const testingRouter = require('./controllers/testing')
|
||||
app.use('/api/testing', testingRouter)
|
||||
}
|
||||
|
||||
app.use(middleware.unknownEndpoint)
|
||||
app.use(middleware.errorHandler)
|
||||
|
||||
|
||||
12
part4/bloglist/controllers/testing.js
Normal file
12
part4/bloglist/controllers/testing.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const testingRouter = require('express').Router()
|
||||
const Blog = require('../models/blog')
|
||||
const User = require('../models/user')
|
||||
|
||||
testingRouter.post('/reset', async (request, response) => {
|
||||
await Blog.deleteMany({})
|
||||
await User.deleteMany({})
|
||||
|
||||
response.status(204).end()
|
||||
})
|
||||
|
||||
module.exports = testingRouter
|
||||
@@ -6,7 +6,9 @@
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=production node index.js",
|
||||
"dev": "cross-env NODE_ENV=development nodemon index.js",
|
||||
"test": "cross-env NODE_ENV=test jest --verbose --runInBand"
|
||||
"test": "cross-env NODE_ENV=test jest --verbose --runInBand",
|
||||
"start:test": "cross-env NODE_ENV=test node index.js"
|
||||
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
||||
@@ -4,6 +4,7 @@ module.exports = {
|
||||
browser: true,
|
||||
es6: true,
|
||||
'jest/globals': true,
|
||||
'cypress/globals': true
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:react/recommended'],
|
||||
parserOptions: {
|
||||
@@ -13,7 +14,7 @@ module.exports = {
|
||||
ecmaVersion: 2018,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['react', 'jest'],
|
||||
plugins: ['react', 'jest', 'cypress'],
|
||||
rules: {
|
||||
indent: ['error', 2],
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
|
||||
0
part5/bloglist-frontend/controllers/testing.js
Normal file
0
part5/bloglist-frontend/controllers/testing.js
Normal file
9
part5/bloglist-frontend/cypress.config.js
Normal file
9
part5/bloglist-frontend/cypress.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { defineConfig } from "cypress";
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
});
|
||||
102
part5/bloglist-frontend/cypress/e2e/spec.cy.js
Normal file
102
part5/bloglist-frontend/cypress/e2e/spec.cy.js
Normal file
@@ -0,0 +1,102 @@
|
||||
describe('blog app', function () {
|
||||
beforeEach(function () {
|
||||
cy.request('POST', 'http://localhost:3001/api/testing/reset')
|
||||
cy.request('POST', 'http://localhost:3001/api/users/', {
|
||||
name: 'Andrew',
|
||||
username: 'andyyyyy',
|
||||
password: '123456',
|
||||
})
|
||||
cy.request('POST', 'http://localhost:3001/api/users/', {
|
||||
name: 'Matti Luukkainen',
|
||||
username: 'mluukkai',
|
||||
password: 'salainen',
|
||||
})
|
||||
cy.visit('http://localhost:3000')
|
||||
})
|
||||
|
||||
it('front page can be opened', function () {
|
||||
cy.contains('Bloglist')
|
||||
cy.contains('Please log in')
|
||||
cy.contains('Username')
|
||||
cy.contains('Password')
|
||||
cy.contains('Login')
|
||||
})
|
||||
|
||||
describe('when logged in', function () {
|
||||
beforeEach(function () {
|
||||
it('login succeeds with correct credentials', function () {
|
||||
cy.get('.username').type('andyyyyy')
|
||||
cy.get('.password').type('123456')
|
||||
cy.get('.loginButton').click()
|
||||
})
|
||||
|
||||
cy.contains('Andrew logged in')
|
||||
})
|
||||
|
||||
it('a new blog can be created', function () {
|
||||
cy.contains('New blog').click()
|
||||
cy.get('.title').type('a blog created')
|
||||
cy.get('.author').type('cypress')
|
||||
cy.get('.urlAddress').type('cypress.com')
|
||||
cy.get('.createButton').click()
|
||||
cy.contains('a blog created by cypress')
|
||||
})
|
||||
|
||||
it('a blog can be liked', function () {
|
||||
cy.contains('New blog').click()
|
||||
cy.get('.title').type('a blog created')
|
||||
cy.get('.author').type('cypress')
|
||||
cy.get('.urlAddress').type('cypress.com')
|
||||
cy.get('.createButton').click()
|
||||
cy.contains('a blog created by cypress')
|
||||
cy.contains('View').click()
|
||||
cy.contains('+').click()
|
||||
cy.contains('1')
|
||||
})
|
||||
|
||||
it('a blog can be deleted', function () {
|
||||
cy.contains('New blog').click()
|
||||
cy.get('.title').type('a blog created')
|
||||
cy.get('.author').type('cypress')
|
||||
cy.get('.urlAddress').type('cypress.com')
|
||||
cy.get('.createButton').click()
|
||||
cy.contains('a blog created by cypress')
|
||||
cy.contains('View').click()
|
||||
cy.contains('Remove').click()
|
||||
cy.get('html').should('not.contain', 'a blog created by cypress')
|
||||
})
|
||||
|
||||
it('only the creator can delete a blog', function () {
|
||||
cy.contains('New blog').click()
|
||||
cy.get('.title').type('a blog created')
|
||||
cy.get('.author').type('cypress')
|
||||
cy.get('.urlAddress').type('cypress.com')
|
||||
cy.get('.createButton').click()
|
||||
cy.contains('a blog created by cypress')
|
||||
cy.get('.logoutButton').click()
|
||||
cy.get('.username').type('mluukkai')
|
||||
cy.get('.password').type('salainen')
|
||||
cy.get('.loginButton').click()
|
||||
cy.contains('View').click()
|
||||
cy.contains('Remove').click()
|
||||
cy.contains('a blog created by cypress')
|
||||
})
|
||||
|
||||
it('blogs are ordered according to likes', function () {
|
||||
cy.contains('New blog').click()
|
||||
cy.get('.title').type('another blog created')
|
||||
cy.get('.author').type('cypress')
|
||||
cy.get('.urlAddress').type('cypress.com')
|
||||
cy.get('.createButton').click()
|
||||
cy.contains('another blog created by cypress')
|
||||
cy.contains('View').click()
|
||||
cy.contains('+').click()
|
||||
cy.wait(1000)
|
||||
cy.contains('+').click()
|
||||
cy.wait(1000)
|
||||
cy.visit('http://localhost:3000')
|
||||
cy.get('.blog').eq(0).should('contain', 'another blog created by cypress')
|
||||
cy.get('.blog').eq(1).should('contain', 'a blog created by cypress')
|
||||
})
|
||||
})
|
||||
})
|
||||
5
part5/bloglist-frontend/cypress/fixtures/example.json
Normal file
5
part5/bloglist-frontend/cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
25
part5/bloglist-frontend/cypress/support/commands.js
Normal file
25
part5/bloglist-frontend/cypress/support/commands.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
20
part5/bloglist-frontend/cypress/support/e2e.js
Normal file
20
part5/bloglist-frontend/cypress/support/e2e.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// ***********************************************************
|
||||
// This example support/e2e.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
2336
part5/bloglist-frontend/package-lock.json
generated
2336
part5/bloglist-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,9 @@
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"eslint": "eslint .",
|
||||
"dev": "NODE_ENV=development nodemon index.js"
|
||||
"dev": "NODE_ENV=development nodemon index.js",
|
||||
"cypress:open": "cypress open",
|
||||
"start:test": "NODE_ENV=test node index.js"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
@@ -41,6 +43,8 @@
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"cypress": "^12.15.0",
|
||||
"eslint-plugin-cypress": "^2.13.3",
|
||||
"eslint-plugin-jest": "^27.2.2",
|
||||
"nodemon": "^2.0.22"
|
||||
},
|
||||
|
||||
@@ -107,13 +107,13 @@ const App = () => {
|
||||
}
|
||||
|
||||
const loginForm = () => (
|
||||
<form onSubmit={handleLogin}>
|
||||
<form onSubmit={handleLogin} className="loginForm">
|
||||
<div>
|
||||
Username
|
||||
<input
|
||||
type="text"
|
||||
value={username}
|
||||
name="username"
|
||||
className="username"
|
||||
onChange={({ target }) => setUsername(target.value)}
|
||||
/>
|
||||
</div>
|
||||
@@ -122,11 +122,11 @@ const App = () => {
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
name="password"
|
||||
className="password"
|
||||
onChange={({ target }) => setPassword(target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
<button type="submit" className='loginButton'>Login</button>
|
||||
</form>
|
||||
)
|
||||
|
||||
@@ -134,19 +134,21 @@ const App = () => {
|
||||
return (
|
||||
<div>
|
||||
<p> {user.name} logged in </p>
|
||||
<button onClick={handleLogout}>Logout</button>
|
||||
<button onClick={handleLogout} className='logoutButton'>Logout</button>
|
||||
<Togglable buttonLabel="New blog" ref={newBlogRef}>
|
||||
<BlogForm createBlog={handleNewBlog} />
|
||||
</Togglable>
|
||||
<h2> All blogs</h2>
|
||||
{blogs.map((blog) => (
|
||||
<Blog
|
||||
key={blog.id}
|
||||
blog={blog}
|
||||
setNotification={setNotification}
|
||||
removeBlog={handleRemoveBlog}
|
||||
/>
|
||||
))}
|
||||
{blogs
|
||||
.sort((a, b) => b.likes - a.likes)
|
||||
.map((blog) => (
|
||||
<Blog
|
||||
key={blog.id}
|
||||
blog={blog}
|
||||
setNotification={setNotification}
|
||||
removeBlog={handleRemoveBlog}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -70,10 +70,10 @@ describe('test for blogs', () => {
|
||||
|
||||
const user = userEvent.setup()
|
||||
|
||||
const title = component.container.getElementsByName('title')
|
||||
const author = component.container.getElementsByName('author')
|
||||
const url = component.container.getElementsByName('urlAddress')
|
||||
const form = component.container.getElementsByName('form')
|
||||
const title = component.container.querySelector('.title')
|
||||
const author = component.container.querySelector('.author')
|
||||
const url = component.container.querySelector('.urlAddress')
|
||||
const form = component.container.querySelector('.form')
|
||||
|
||||
await user.type(title, 'How to be a good programmer')
|
||||
await user.type(author, 'Andrew Trieu')
|
||||
|
||||
@@ -24,13 +24,13 @@ export const BlogForm = ({ createBlog }) => {
|
||||
return (
|
||||
<div>
|
||||
<h2> Create new blog</h2>
|
||||
<form onSubmit={addBlog} name='form'>
|
||||
<form onSubmit={addBlog} className='form'>
|
||||
<div>
|
||||
Title:
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
name="title"
|
||||
className="title"
|
||||
onChange={(event) => setTitle(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
@@ -39,7 +39,7 @@ export const BlogForm = ({ createBlog }) => {
|
||||
<input
|
||||
type="text"
|
||||
value={author}
|
||||
name="author"
|
||||
className="author"
|
||||
onChange={(event) => setAuthor(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
@@ -48,11 +48,11 @@ export const BlogForm = ({ createBlog }) => {
|
||||
<input
|
||||
type="text"
|
||||
value={urlAddress}
|
||||
name="urlAddress"
|
||||
className="urlAddress"
|
||||
onChange={(event) => setUrlAddress(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button type="submit">Create</button>
|
||||
<button type="submit" className='createButton'>Create</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user