Added control page tests and fixed bugs

This commit is contained in:
Yuriy Kulakov 2021-05-07 22:29:50 +03:00
parent fe33c8d042
commit e32f12019e
5 changed files with 157 additions and 182 deletions

View File

@ -15,11 +15,6 @@
{% block content %} {% block content %}
<div class="container-md"> <div class="container-md">
<div class="new-section">
<p class="row page-description" id="licences_remaining">Свободных Мест:</p>
</div>
{% for message in messages %} {% for message in messages %}
<script>create_notification('{{message}}','','{{message.tags}}',2000)</script> <script>create_notification('{{message}}','','{{message.tags}}',2000)</script>
{% endfor %} {% endfor %}
@ -27,75 +22,15 @@
{% block form %} {% block form %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="row justify-content-center new-section"> <div class="row justify-content-center new-section">
<div class="col-10"> <div class="col-10">
<h6 class="table-title">Список сотрудников</h6> <h6 class="table-title">Список сотрудников</h6>
{% block table %} {% block table %}
<table class="table table-dark light-table"> <div id="table"></div>
<thead>
<th>
<input
type="checkbox"
class="form-check-input"
id="head-checkbox"
/>
</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</thead>
<tbody id="tbody"></tbody>
</table>
<p id="loading">Данные загружаются...</p>
{% endblock %} {% endblock %}
</div> </div>
</div> </div>
{% block count %}
<div class="row justify-content-center new-section">
<div class="col-5">
<div class="info">
<div class="info-row">
<div class="info-target">Инженеров:</div>
<div class="info-quantity">
<div class="status-circle-small light-green"></div>
<span class="info-quantity-value">{{ engineers }}</span>
</div>
</div>
<div class="info-row">
<div class="info-target">Легких агентов:</div>
<div class="info-quantity">
<div class="status-circle-small light-yellow"></div>
<span class="info-quantity-value">{{ light_agents }}</span>
</div>
</div>
</div>
</div>
{% endblock %}
{% block buttons %}
<div class="col-5">
<button type="submit" name="engineer" class="request-acess-button default-button">
Назначить выбранных на роль инженера
</button>
<button type="submit" name="light_agent" class="hand-over-acess-button default-button">
Назначить выбранных на роль легкого агента
</button>
</div>
{% endblock %}
</div>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,95 +1,84 @@
import React from "react"; import React from "react";
import {render, unmountComponentAtNode} from "react-dom"; import {render, unmountComponentAtNode} from "react-dom";
import {act} from "react-dom/test-utils"; import {act} from "react-dom/test-utils";
import {TableBody} from "../src/control"; import {Table} from "../src/control";
import * as test_data from "./test_users.json" import * as test_data from "./test_users.json"
import axios from "axios"; import axios from "axios";
import MockAdapter from "axios-mock-adapter";
let mock;
let container = null let container = null
let table = null
let load = null
let engineer_count = null
let agents_count = null
let licences_remaining = null
jest.mock("axios", () => {
return {
get: jest.fn(() => Promise.resolve())
};
});
beforeEach(() => { beforeEach(() => {
table = document.createElement("table"); mock = new MockAdapter(axios);
container = document.createElement("tbody"); mock.onGet('/api/users').reply(200, test_data)
container.id = "tbody"; container = document.createElement('div')
table.appendChild(container); container.id = "table"
load = document.createElement("p"); document.body.appendChild(container)
load.id = "loading" })
load.innerHTML = "Данные загружаются";
licences_remaining = document.createElement('p')
licences_remaining.id = "licences_remaining"
engineer_count = document.createElement("p")
agents_count = document.createElement("p")
engineer_count.className = "info-quantity-value"
agents_count.className = "info-quantity-value"
document.body.appendChild(table)
document.body.appendChild(engineer_count)
document.body.appendChild(agents_count)
document.body.appendChild(licences_remaining)
document.body.appendChild(load)
});
afterEach(() => { afterEach(() => {
unmountComponentAtNode(container); unmountComponentAtNode(container);
mock.restore()
container.remove(); container.remove();
table.remove();
engineer_count.remove()
agents_count.remove()
licences_remaining.remove()
container = null; container = null;
table = null;
load = null;
engineer_count = null
agents_count = null
licences_remaining = null
}); });
describe("testing table", (done) => {
it("has only main table row without axios request", () => {
act(() => {
render(<TableBody/>, container);
});
expect(container.getElementsByTagName("tr").length).toBe(1);
});
it("shows valid number of free workplaces", async () => { it("has only main table row without axios request", () => {
axios.get.mockImplementation(() => Promise.resolve({data: test_data})); act(() => {
await act(async () => { render(<Table/>, container);
render(<TableBody/>, container)
})
let licences = Number(licences_remaining.textContent.replace(/Свободных мест: /, ''))
expect(licences).toEqual(1)
}); });
let tbody = container.querySelector("#tbody")
expect(tbody.getElementsByTagName('tr').length).toBe(0);
});
it("Pretext must be deleted on render", () => { it("shows valid number of free workplaces", async () => {
act(() => { await act(async () => {
render(<TableBody/>, container) render(<Table/>, container)
}) })
expect(document.body).not.toContain(load) let element = container.querySelector('#licences_remaining')
}); let licences = Number(element.innerHTML.replace(/Свободных мест: /, ''))
expect(licences).toEqual(1)
});
it("has valid number of table rows with axios request", async () => { it("Pretext must be deleted on render", () => {
axios.get.mockImplementation(() => Promise.resolve({data: test_data})); act(() => {
await act(async () => { render(<Table/>, container)
render(<TableBody/>, container) })
}) expect(document.body).not.toContain(container.querySelector('#loading'))
expect(container.getElementsByTagName("tr").length) });
.toEqual(test_data.users.length + test_data.zendesk_users.length + 1)
});
it("show valid number for engineers and light agents", async () => { it("has valid number of table rows with axios request", async () => {
axios.get.mockImplementation(() => Promise.resolve({data: test_data})); await act(async () => {
await act(async () => { render(<Table/>, container)
render(<TableBody/>, container) })
}) let tbody = container.querySelector("#tbody")
expect(Number(engineer_count.textContent)).toEqual(test_data.engineers) expect(tbody.getElementsByTagName('tr').length)
expect(Number(agents_count.textContent)).toEqual(test_data.light_agents) .toEqual(test_data.users.length + test_data.zendesk_users.length)
}); });
it("show valid number for engineers and light agents", async () => {
await act(async () => {
render(<Table/>, container)
})
let engineers = container.querySelector('#engineers')
let agents = container.querySelector('#agents')
expect(Number(engineers.textContent)).toEqual(test_data.engineers)
expect(Number(agents.textContent)).toEqual(test_data.light_agents)
});
it("called one request on mount", async () => {
let req = jest.spyOn(Table.prototype, "get_users")
await act(async () => {
render(<Table/>, container)
})
expect(req).toHaveBeenCalledTimes(1)
})
it("checkbox count equals users from db count", async () => {
await act(async () => {
render(<Table/>, container)
})
let tbody = container.querySelector("#tbody")
let checkboxes = tbody.querySelectorAll("input[type='checkbox']")
let users = test_data.users
expect(checkboxes.length).toEqual(users.length)
}) })

View File

@ -3,9 +3,6 @@ module.exports = {
testPathIgnorePatterns: [ testPathIgnorePatterns: [
"./node_modules/" "./node_modules/"
], ],
unmockedModulePathPatterns: [
"./node_modules/react"
],
roots: [ roots: [
"./__tests__" "./__tests__"
], ],

View File

@ -1,6 +1,48 @@
import React from "react"; import React, {useState} from "react";
import ReactDOM from "react-dom";
import axios from "axios"; import axios from "axios";
import * as ReactDOM from "react-dom";
function FreeWorkplaces(props) {
return (
<div className="new-section">
<p className="row page-description" id="licences_remaining">Свободных мест: {props.count}</p>
</div>
)
}
function WorkersCount(props) {
return (
<div className="row justify-content-center new-section d-flex align-items-center">
<div className="col-5">
<div className="info">
<div className="info-row">
<div className="info-target">Инженеров:</div>
<div className="info-quantity">
<div className="status-circle-small light-green"></div>
<span className="info-quantity-value" id="engineers">{props.engineers}</span>
</div>
</div>
<div className="info-row">
<div className="info-target">Легких агентов:</div>
<div className="info-quantity">
<div className="status-circle-small light-yellow"></div>
<span className="info-quantity-value" id="agents">{props.light_agents}</span>
</div>
</div>
</div>
</div>
<div className="col-5">
<button type="submit" name="engineer" className="request-acess-button default-button">
Назначить выбранных на роль инженера
</button>
<button type="submit" name="light_agent" className="hand-over-acess-button default-button">
Назначить выбранных на роль легкого агента
</button>
</div>
</div>
)
}
class ModelUserTableRow extends React.Component { class ModelUserTableRow extends React.Component {
render() { render() {
@ -26,11 +68,10 @@ class ModelUserTableRow extends React.Component {
class ModelUserTableRows extends React.Component { class ModelUserTableRows extends React.Component {
render() { render() {
return ReactDOM.createPortal( return (
this.props.users.map((user, key) => ( this.props.users.map((user, key) => (
<ModelUserTableRow user={user} key={key} /> <ModelUserTableRow user={user} key={key} />
)), ))
document.getElementById("tbody")
); );
} }
} }
@ -58,39 +99,27 @@ class ZendeskUserTableRow extends React.Component {
class ZendeskUserTableRows extends React.Component { class ZendeskUserTableRows extends React.Component {
render() { render() {
return ReactDOM.createPortal( return (
this.props.users.map((user, key) => ( this.props.users.map((user, key) => (
<ZendeskUserTableRow user={user} key={key} /> <ZendeskUserTableRow user={user} key={key} />
)), ))
document.getElementById("tbody") )
);
} }
} }
export class TableBody extends React.Component { export class Table extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
users: [], users: [],
engineers: 0, engineers: null,
light_agents: 0, light_agents: null,
zendesk_users: [], zendesk_users: [],
max_agents: 3, max_agents: null,
renderLoad: true
}; };
} }
change_elements_html() {
let elements = document.querySelectorAll(".info-quantity-value");
let licences = document.getElementById("licences_remaining");
elements[0].innerHTML = this.state.engineers;
elements[1].innerHTML = this.state.light_agents;
let max_licences = Math.max(
this.state.max_agents - this.state.engineers,
0
);
licences.innerHTML = "Свободных мест: " + max_licences;
}
async get_users() { async get_users() {
await axios.get("/api/users").then((response) => { await axios.get("/api/users").then((response) => {
this.setState({ this.setState({
@ -99,19 +128,23 @@ export class TableBody extends React.Component {
light_agents: response.data.light_agents, light_agents: response.data.light_agents,
zendesk_users: response.data.zendesk_users, zendesk_users: response.data.zendesk_users,
max_agents: response.data.max_agents, max_agents: response.data.max_agents,
renderLoad: false
}); });
return response
}).catch(reason => {
console.log(reason)
}); });
this.change_elements_html();
}
delete_pretext() {
document.getElementById("loading").remove();
} }
componentDidMount() { componentDidMount() {
this.get_users().then(() => this.delete_pretext()); this.get_users().then(() => {})
.catch(reason => {
console.log(reason)
});
this.interval = setInterval(() => { this.interval = setInterval(() => {
this.get_users(); this.get_users().catch(reason => {
console.log(reason)
})
}, 60000); }, 60000);
} }
@ -121,10 +154,31 @@ export class TableBody extends React.Component {
render() { render() {
return ( return (
<tr> <div>
<ModelUserTableRows users={this.state.users} /> <FreeWorkplaces count={Math.max(this.state.max_agents - this.state.engineers, 0)}/>
<ZendeskUserTableRows users={this.state.zendesk_users} /> <table className="table table-dark light-table">
</tr> <thead>
<tr>
<th>
<input
type="checkbox"
className="form-check-input"
id="head-checkbox"
/>
</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody id="tbody">
<ModelUserTableRows users={this.state.users}/>
<ZendeskUserTableRows users={this.state.zendesk_users}/>
</tbody>
</table>
{this.state.renderLoad === true ? <p id="loading">Данные загружаются...</p> : null}
<WorkersCount engineers={this.state.engineers} light_agents={this.state.light_agents}/>
</div>
); );
} }
} }

View File

@ -1,4 +1,4 @@
import {TableBody} from "./control" import {Table} from "./control"
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import React from "react"; import React from "react";
@ -12,5 +12,5 @@ function head_checkbox() {
} }
ReactDOM.render(<TableBody />, document.getElementById("tbody")); ReactDOM.render(<Table />, document.getElementById("table"));
head_checkbox(); head_checkbox();