├── .gitignore
├── index.js
├── webpack.config.js
├── test
├── setup-mocha.js
├── queue.spec.js
├── node.spec.js
└── max-heap.spec.js
├── index.html
├── src
├── node.js
├── queue.js
└── max-heap.js
├── package.json
├── score-weight.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | app.bundle.js
4 | app.bundle.js.map
5 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const MaxHeap = require('./src/max-heap');
2 |
3 | const h = new MaxHeap();
4 | window.h = h;
5 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: { app: './index.js' },
3 | output: {
4 | path: './',
5 | filename: 'app.bundle.js'
6 | },
7 | devtool: 'source-map'
8 | };
9 |
--------------------------------------------------------------------------------
/test/setup-mocha.js:
--------------------------------------------------------------------------------
1 | const chai = require('chai');
2 | const sinon = require('sinon');
3 | const sinonChai = require('sinon-chai');
4 |
5 | global.expect = chai.expect;
6 | global.sinon = sinon;
7 | chai.use(sinonChai);
8 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/node.js:
--------------------------------------------------------------------------------
1 | class Node {
2 | constructor(data, priority) {
3 |
4 | }
5 |
6 | appendChild(node) {
7 |
8 | }
9 |
10 | removeChild(node) {
11 |
12 | }
13 |
14 | remove() {
15 |
16 | }
17 |
18 | swapWithParent() {
19 |
20 | }
21 | }
22 |
23 | module.exports = Node;
24 |
--------------------------------------------------------------------------------
/src/queue.js:
--------------------------------------------------------------------------------
1 | const MaxHeap = require('./max-heap.js');
2 |
3 | class PriorityQueue {
4 | constructor(maxSize) {
5 |
6 | }
7 |
8 | push(data, priority) {
9 |
10 | }
11 |
12 | shift() {
13 |
14 | }
15 |
16 | size() {
17 |
18 | }
19 |
20 | isEmpty() {
21 |
22 | }
23 | }
24 |
25 | module.exports = PriorityQueue;
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "priority-queue",
3 | "version": "1.0.0",
4 | "description": "",
5 | "scripts": {
6 | "test": "mocha -r ./test/setup-mocha.js",
7 | "start": "webpack-dev-server"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "devDependencies": {
12 | "chai": "^3.5.0",
13 | "mocha": "^3.0.2",
14 | "sinon": "^1.17.5",
15 | "sinon-chai": "^2.8.0",
16 | "webpack": "^1.13.1",
17 | "webpack-dev-server": "^1.14.1"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/max-heap.js:
--------------------------------------------------------------------------------
1 | const Node = require('./node');
2 |
3 | class MaxHeap {
4 | constructor() {
5 |
6 | }
7 |
8 | push(data, priority) {
9 |
10 | }
11 |
12 | pop() {
13 |
14 | }
15 |
16 | detachRoot() {
17 |
18 | }
19 |
20 | restoreRootFromLastInsertedNode(detached) {
21 |
22 | }
23 |
24 | size() {
25 |
26 | }
27 |
28 | isEmpty() {
29 |
30 | }
31 |
32 | clear() {
33 |
34 | }
35 |
36 | insertNode(node) {
37 |
38 | }
39 |
40 | shiftNodeUp(node) {
41 |
42 | }
43 |
44 | shiftNodeDown(node) {
45 |
46 | }
47 | }
48 |
49 | module.exports = MaxHeap;
50 |
--------------------------------------------------------------------------------
/score-weight.json:
--------------------------------------------------------------------------------
1 | {
2 | "Node": {
3 | "weight": 10,
4 | "suitesWeights": {
5 | "#constructor": 10,
6 | "#appendChild": 25,
7 | "#removeChild": 25,
8 | "#remove": 10,
9 | "#swapWithParent": 30
10 | }
11 | },
12 | "MaxHeap": {
13 | "weight": 30,
14 | "suitesWeights": {
15 | "#constructor": 10,
16 | "#push": 10,
17 | "#insertNode": 10,
18 | "#shiftNodeUp": 30,
19 | "#clear": 5,
20 | "#pop": 10,
21 | "#detachRoot": 10,
22 | "#restoreRootFromLastInsertedNode": 10,
23 | "#shiftNodeDown": 30,
24 | "#size": 5,
25 | "#isEmpty": 5
26 | }
27 | },
28 | "PriorityQueue": {
29 | "weight": 60,
30 | "suitesWeights": {
31 | "#constructor": 1,
32 | "#push": 10,
33 | "#shift": 50,
34 | "#size": 1,
35 | "#isEmpty": 1
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Priority queue task
2 |
3 | :warning: DO NOT SUBMIT PRS TO THIS REPO :warning:
4 |
5 | ### Task
6 |
7 | The task should be implemeted using [tdd](https://en.wikipedia.org/wiki/Test-driven_development)
8 |
9 | All requirements are defined in the tests.
10 |
11 | P S A partial solution is also a solution. The task is very long )
12 |
13 | ### Run tests
14 | ```sh
15 | npm test
16 | ```
17 |
18 | ### Run in browser
19 | ```sh
20 | npm start
21 | ```
22 |
23 | open http://localhost:8080
24 |
25 | ---
26 |
27 | © [R1ZZU](https://github.com/R1ZZU)
28 |
29 | ### Prerequisites
30 | 1. Install [Node.js](https://nodejs.org/en/download/)
31 | 2. Fork this repository: https://github.com/rolling-scopes-school/priority-queue/
32 | 3. Clone your newly created repo: https://github.com/<%your_github_username%>/priority-queue/
33 | 4. Go to folder `priority-queue`
34 | 5. To install all dependencies use [`npm install`](https://docs.npmjs.com/cli/install)
35 | 6. Run `npm test` in command line or you can run tests in browser with `npm start` and open http://localhost:8080
36 | 7. You will see the number of passing and failing tests. 100% of passing tests is equal to 100p in score
37 |
38 | ---
39 |
40 | ### Submit to [rs app](https://app.rs.school)
41 | 1. Open [rs app](https://app.rs.school) and login
42 | 2. Go to [submit task page](https://app.rs.school/course/submit-task?course=rs-2019-q3)
43 | 3. Select your task (priority-queue)
44 | 4. Press the submit button and enjoy
45 |
46 | ---
47 |
48 | ### Notes
49 | 1. We recommend you to use nodejs of version 10 or lower. If you using any of features that does not supported by node v10, score won't be submitted.
50 | 2. Please be sure that each of your test in limit of 30sec.
51 |
--------------------------------------------------------------------------------
/test/queue.spec.js:
--------------------------------------------------------------------------------
1 | const Queue = require('../src/queue');
2 | const MaxHeap = require('../src/max-heap');
3 |
4 | describe('PriorityQueue', () => {
5 | describe('#constructor', () => {
6 | it('assings passed maxSize or set it to default value 30', () => {
7 | const q = new Queue(10);
8 | const qWithDefaultMaxSize = new Queue();
9 |
10 | expect(q.maxSize).to.equal(10);
11 | expect(qWithDefaultMaxSize.maxSize).to.equal(30);
12 | });
13 |
14 | it('assings new MaxHeap to this.heap', () => {
15 | const q = new Queue();
16 |
17 | expect(q.heap).to.be.instanceof(MaxHeap);
18 | });
19 | });
20 |
21 | describe('#push', () => {
22 | let q;
23 |
24 | beforeEach(() => {
25 | q = new Queue(3);
26 | });
27 |
28 | it('calls heap.push with passed data and priority', () => {
29 | sinon.spy(q.heap, 'push');
30 |
31 | q.push(0, 1);
32 | expect(q.heap.push).to.have.been.calledWith(0, 1);
33 | });
34 |
35 | it('throws an error if queue has max size', () => {
36 | q.push(0, 1);
37 | q.push(1, 2);
38 | q.push(2, 3);
39 |
40 | expect(() => {
41 | q.push(3, 4);
42 | }).to.throw();
43 | });
44 | });
45 |
46 | describe('#shift', () => {
47 | let q;
48 |
49 | beforeEach(() => {
50 | q = new Queue();
51 | });
52 |
53 | it('calls heap.pop', () => {
54 | q.push(0, 1);
55 |
56 | sinon.spy(q.heap, 'pop');
57 | q.shift();
58 | expect(q.heap.pop).to.have.been.called;
59 | });
60 |
61 | it('returns value of removed node', () => {
62 | q.push(0, 1);
63 | expect(q.shift()).to.equal(0);
64 | });
65 |
66 | it('throws an error if queue is empty', () => {
67 | expect(() => {
68 | q.shift()
69 | }).to.throw();
70 | });
71 |
72 | it('should return items sorted by priority', () => {
73 | const nodes = [
74 | { priority: 10, data: 1 },
75 | { priority: 20, data: 2 },
76 | { priority: 5, data: 3 },
77 | { priority: 0, data: 4 },
78 | { priority: 8, data: 5 },
79 | { priority: 12, data: 6 },
80 | { priority: 17, data: 7 },
81 | { priority: 15, data: 8 },
82 | ];
83 |
84 | const expectedData = [2, 7, 8, 6, 1, 5, 3, 4]
85 |
86 | nodes.forEach(node => q.push(node.data, node.priority));
87 | expectedData.forEach(d => expect(q.shift()).to.equal(d));
88 | });
89 |
90 | it('should handle items with same priority (return in the same order this items have been added)', () => {
91 | const expectedData = [3, 5, 1, 0, 4, 2];
92 |
93 | q.push(0, 10);
94 | q.push(1, 15);
95 | q.push(2, 4);
96 | q.push(3, 17);
97 | q.push(4, 6);
98 | q.push(5, 17);
99 |
100 | for (var i = 0; i < 6; i++) {
101 | expect(q.shift()).to.equal(expectedData[i]);
102 | }
103 | });
104 | });
105 |
106 | describe('#size', () => {
107 | it('returns current size of queue', () => {
108 | const q = new Queue();
109 |
110 | q.push(0, 1);
111 | expect(q.size()).to.equal(1);
112 |
113 | q.push(1, 2);
114 | expect(q.size()).to.equal(2);
115 |
116 | q.push(2, 3);
117 | expect(q.size()).to.equal(3);
118 |
119 | q.shift();
120 | q.shift();
121 |
122 | expect(q.size()).to.equal(1);
123 |
124 | q.shift();
125 | expect(q.size()).to.equal(0);
126 | });
127 | });
128 |
129 | describe('#isEmpty', () => {
130 | it('return true if queue is empty', () => {
131 | const q = new Queue();
132 |
133 | expect(q.isEmpty()).to.equal(true);
134 |
135 | q.push(0, 1);
136 | expect(q.isEmpty()).to.equal(false);
137 |
138 | q.shift();
139 | expect(q.isEmpty()).to.equal(true);
140 | });
141 | });
142 | });
143 |
--------------------------------------------------------------------------------
/test/node.spec.js:
--------------------------------------------------------------------------------
1 | const Node = require('../src/node');
2 |
3 | describe('Node', () => {
4 | describe('#constructor', () => {
5 | const node = new Node(42, 15);
6 |
7 | it('assigns passed data and priority to this', () => {
8 | expect(node.data).to.equal(42);
9 | expect(node.priority).to.equal(15);
10 | });
11 |
12 | it('assigns this.parent, this.left and this.right to null', () => {
13 | expect(node.parent).to.equal(null);
14 | expect(node.left).to.equal(null);
15 | expect(node.right).to.equal(null);
16 | });
17 | });
18 |
19 | describe('#appendChild', () => {
20 | let parent, leftChild, rightChild;
21 |
22 | beforeEach(() => {
23 | parent = new Node(42, 15);
24 | leftChild = new Node(13, 20);
25 | rightChild = new Node(98, 69);
26 | });
27 |
28 | it('assigns passed child to this.left', () => {
29 | parent.appendChild(leftChild);
30 |
31 | expect(parent.left).to.equal(leftChild);
32 | expect(parent.right).to.equal(null);
33 | });
34 |
35 | it('assigns passed child to this.right if this.left exists', () => {
36 | parent.appendChild(leftChild);
37 | parent.appendChild(rightChild);
38 |
39 | expect(parent.left).to.equal(leftChild);
40 | expect(parent.right).to.equal(rightChild);
41 | });
42 |
43 | it('does nothing if this.left and this.right exist', () => {
44 | parent.appendChild(leftChild);
45 | parent.appendChild(rightChild);
46 | parent.appendChild(new Node(42, 15));
47 |
48 | expect(parent.left).to.equal(leftChild);
49 | expect(parent.right).to.equal(rightChild);
50 | });
51 | });
52 |
53 | describe('#removeChild', () => {
54 | let parent, leftChild, rightChild;
55 |
56 | beforeEach(() => {
57 | parent = new Node(42, 15);
58 | leftChild = new Node(13, 20);
59 | rightChild = new Node(98, 69);
60 |
61 | parent.appendChild(leftChild);
62 | parent.appendChild(rightChild);
63 | });
64 |
65 | it('assing null to this.left if passed node is left child', () => {
66 | parent.removeChild(leftChild);
67 | expect(parent.left).to.equal(null);
68 | });
69 |
70 | it('assing null to this.right if passed node is right child', () => {
71 | parent.removeChild(rightChild);
72 | expect(parent.right).to.equal(null);
73 | });
74 |
75 | it('throws error if passed node is not a child of this node', () => {
76 | expect(() => {
77 | parent.removeChild(new Node(15, 42));
78 | }).to.throw();
79 |
80 | expect(parent.left).to.equal(leftChild);
81 | expect(parent.right).to.equal(rightChild);
82 | });
83 |
84 | it('assigns null to child.parent', () => {
85 | parent.removeChild(leftChild);
86 |
87 | expect(leftChild.parent).to.equal(null);
88 | })
89 | });
90 |
91 | describe('#remove', () => {
92 | it('does nothing if node does not have parent', () => {
93 | const node = new Node(42, 15);
94 |
95 | expect(() => {
96 | node.remove();
97 | }).not.to.throw();
98 | });
99 |
100 | it('calls child.parent.removeChild with child as arg', () => {
101 | const parent = new Node(42, 15);
102 | const child = new Node(15, 42);
103 |
104 | parent.appendChild(child);
105 |
106 | sinon.spy(parent, 'removeChild');
107 |
108 | child.remove();
109 |
110 | expect(parent.removeChild).to.have.been.calledOnce;
111 | expect(parent.removeChild).to.have.been.calledWith(child);
112 | });
113 | });
114 |
115 | describe('#swapWithParent', () => {
116 | it('does nothing if node does not have parent', () => {
117 | const node = new Node(15, 42);
118 |
119 | expect(() => {
120 | node.swapWithParent();
121 | }).not.to.throw();
122 | });
123 |
124 | it('updates parent.parent', () => {
125 | const parent = new Node(15, 42);
126 | const child = new Node(42, 15);
127 |
128 | parent.appendChild(child);
129 | child.swapWithParent();
130 |
131 | expect(parent.parent).to.equal(child);
132 | });
133 |
134 | it('updates parent.parent.parent', () => {
135 | const root = new Node(8, 8);
136 | const child = new Node(4, 4);
137 | const grandson = new Node(2, 2);
138 |
139 | root.appendChild(child);
140 | child.appendChild(grandson);
141 |
142 | grandson.swapWithParent();
143 |
144 | expect(child.parent).to.equal(grandson);
145 | expect(grandson.parent).to.equal(root);
146 | });
147 |
148 | it('updates child.parent', () => {
149 | const parentOfParent = new Node(100, 500);
150 | const parent = new Node(15, 42);
151 | const child = new Node(42, 15);
152 |
153 | parentOfParent.appendChild(parent);
154 | parent.appendChild(child);
155 | child.swapWithParent();
156 |
157 | expect(child.parent).to.equal(parentOfParent);
158 | });
159 |
160 | it('updates parent.child.parent', () => {
161 | const root = new Node(1, 2);
162 | const left = new Node(3, 4);
163 | const right = new Node(5, 6);
164 |
165 | root.appendChild(left);
166 | root.appendChild(right);
167 |
168 | right.swapWithParent();
169 |
170 | expect(left.parent).to.equal(right);
171 | })
172 |
173 | it('updates children of node and parent node', () => {
174 | const root = new Node(42, 15);
175 | const left = new Node(13, 42);
176 | const right = new Node(0, 1);
177 | const childOfLeft = new Node(0, 15);
178 |
179 | root.appendChild(left);
180 | root.appendChild(right);
181 | left.appendChild(childOfLeft);
182 |
183 | left.swapWithParent();
184 |
185 | expect(left.right).to.equal(right);
186 | expect(left.left).to.equal(root);
187 | expect(root.left).to.equal(childOfLeft);
188 | });
189 |
190 | it('maintains correct state of parent.parent.left and parent.parent.right', () => {
191 | const root = new Node(15, 42);
192 | const left = new Node(42, 15);
193 | const right = new Node(13, 42);
194 | const childOfLeft = new Node(13, 34);
195 | const childOfRight = new Node(0, 1);
196 |
197 | root.appendChild(left);
198 | root.appendChild(right);
199 | left.appendChild(childOfLeft);
200 | right.appendChild(childOfRight);
201 |
202 | childOfLeft.swapWithParent();
203 | childOfRight.swapWithParent();
204 |
205 | expect(root.left).to.equal(childOfLeft);
206 | expect(root.right).to.equal(childOfRight);
207 | });
208 | });
209 | });
210 |
--------------------------------------------------------------------------------
/test/max-heap.spec.js:
--------------------------------------------------------------------------------
1 | const Node = require('../src/node');
2 | const MaxHeap = require('../src/max-heap');
3 |
4 | describe('MaxHeap', () => {
5 | describe('#constructor', () => {
6 | const h = new MaxHeap();
7 |
8 | it('assigns null to this.root', () => {
9 | expect(h.root).to.equal(null);
10 | });
11 |
12 | it('assigns [] to this.parentNodes', () => {
13 | expect(h.parentNodes).to.deep.equal([]);
14 | });
15 | });
16 |
17 | describe('#push', () => {
18 | let h;
19 |
20 | beforeEach(() => {
21 | h = new MaxHeap();
22 | });
23 |
24 | it('calls insertNode with new node having passed data and priority', () => {
25 | sinon.spy(h, 'insertNode');
26 |
27 | h.push(42, 15);
28 |
29 | expect(h.insertNode).to.have.been.calledOnce;
30 | expect(h.insertNode.firstCall.args[0]).to.be.an.instanceof(Node);
31 | expect(h.insertNode.firstCall.args[0].data).to.equal(42);
32 | expect(h.insertNode.firstCall.args[0].priority).to.equal(15);
33 | });
34 |
35 | it('calls shiftNodeUp with new node having passed data and priority', () => {
36 | sinon.spy(h, 'shiftNodeUp');
37 |
38 | h.push(42, 15);
39 |
40 | expect(h.shiftNodeUp).to.have.been.calledOnce;
41 | expect(h.shiftNodeUp.firstCall.args[0]).to.be.an.instanceof(Node);
42 | expect(h.shiftNodeUp.firstCall.args[0].data).to.equal(42);
43 | expect(h.shiftNodeUp.firstCall.args[0].priority).to.equal(15);
44 | });
45 | });
46 |
47 | describe('#insertNode', () => {
48 | let h;
49 |
50 | beforeEach(() => {
51 | h = new MaxHeap();
52 | });
53 |
54 | it('assings passed node to this.root if heap is empty', () => {
55 | const node = new Node(42, 15);
56 |
57 | h.insertNode(node);
58 | expect(h.root).to.equal(node);
59 | });
60 |
61 | it('inserts nodes to correct places', () => {
62 | const nodes = [
63 | new Node(0, 0),
64 | new Node(1, 1),
65 | new Node(2, 2),
66 | new Node(3, 3),
67 | new Node(4, 4),
68 | new Node(5, 5),
69 | new Node(6, 6),
70 | ];
71 |
72 | nodes.forEach(node => {
73 | h.insertNode(node);
74 | });
75 |
76 | expect(h.root).to.equal(nodes[0]);
77 | expect(h.root.left).to.equal(nodes[1]);
78 | expect(h.root.right).to.equal(nodes[2]);
79 | expect(h.root.left.left).to.equal(nodes[3]);
80 | expect(h.root.left.right).to.equal(nodes[4]);
81 | });
82 |
83 | it('maintains this.parentNodes in correct state', () => {
84 | const nodes = [
85 | new Node(0, 0),
86 | new Node(1, 1),
87 | new Node(2, 2),
88 | new Node(3, 3),
89 | new Node(4, 4),
90 | new Node(5, 5),
91 | new Node(6, 6),
92 | ];
93 |
94 | h.insertNode(nodes[0]);
95 | expect(h.parentNodes[0]).to.equal(nodes[0]);
96 |
97 | h.insertNode(nodes[1]);
98 | expect(h.parentNodes[0]).to.equal(nodes[0]);
99 | expect(h.parentNodes[1]).to.equal(nodes[1]);
100 |
101 | h.insertNode(nodes[2]);
102 | expect(h.parentNodes[0]).to.equal(nodes[1]);
103 | expect(h.parentNodes[1]).to.equal(nodes[2]);
104 |
105 | h.insertNode(nodes[3]);
106 | expect(h.parentNodes[0]).to.equal(nodes[1]);
107 | expect(h.parentNodes[1]).to.equal(nodes[2]);
108 | expect(h.parentNodes[2]).to.equal(nodes[3]);
109 |
110 | h.insertNode(nodes[4]);
111 | expect(h.parentNodes[0]).to.equal(nodes[2]);
112 | expect(h.parentNodes[1]).to.equal(nodes[3]);
113 | expect(h.parentNodes[2]).to.equal(nodes[4]);
114 |
115 | h.insertNode(nodes[5]);
116 | expect(h.parentNodes[0]).to.equal(nodes[2]);
117 | expect(h.parentNodes[1]).to.equal(nodes[3]);
118 | expect(h.parentNodes[2]).to.equal(nodes[4]);
119 | expect(h.parentNodes[3]).to.equal(nodes[5]);
120 |
121 | h.insertNode(nodes[6]);
122 | expect(h.parentNodes[0]).to.equal(nodes[3]);
123 | expect(h.parentNodes[1]).to.equal(nodes[4]);
124 | expect(h.parentNodes[2]).to.equal(nodes[5]);
125 | expect(h.parentNodes[3]).to.equal(nodes[6]);
126 | });
127 | });
128 |
129 | describe('#shiftNodeUp', () => {
130 | let h;
131 |
132 | beforeEach(() => {
133 | h = new MaxHeap();
134 |
135 | h.root = new Node(0, 10);
136 | h.root.appendChild(new Node(1, 5));
137 | h.root.appendChild(new Node(2, 7));
138 | h.root.left.appendChild(new Node(3, 20));
139 |
140 | /**
141 | 10 20
142 | / \ / \
143 | 5 7 - shift up -> 10 7
144 | / /
145 | 20 5
146 | **/
147 |
148 | h.parentNodes = [
149 | h.root.left,
150 | h.root.right,
151 | h.root.left.left,
152 | ];
153 | });
154 |
155 | it('shifts node up until heap property is valid', () => {
156 | const newRoot = h.root.left.left;
157 | h.shiftNodeUp(h.root.left.left);
158 | expect(h.root).to.equal(newRoot);
159 | });
160 |
161 | it('maintants parentNodes in correct state', () => {
162 | const correctParentNodesOrderAfterShiftUp = [
163 | h.root,
164 | h.root.right,
165 | h.root.left
166 | ]
167 |
168 | h.shiftNodeUp(h.root.left.left);
169 |
170 | expect(h.parentNodes[0]).to.equal(correctParentNodesOrderAfterShiftUp[0]);
171 | expect(h.parentNodes[1]).to.equal(correctParentNodesOrderAfterShiftUp[1]);
172 | expect(h.parentNodes[2]).to.equal(correctParentNodesOrderAfterShiftUp[2]);
173 | });
174 |
175 | it('calls Node.swapWithParent', () => {
176 | const nodeToShiftUp = h.root.left.left;
177 | sinon.spy(nodeToShiftUp, 'swapWithParent');
178 |
179 | h.shiftNodeUp(nodeToShiftUp);
180 |
181 | expect(nodeToShiftUp.swapWithParent).to.have.been.calledTwice;
182 | });
183 |
184 | it('calls itself recursively', () => {
185 | const nodeToShiftUp = h.root.left.left;
186 | sinon.spy(h, 'shiftNodeUp');
187 |
188 | h.shiftNodeUp(nodeToShiftUp);
189 |
190 | expect(h.shiftNodeUp).to.have.been.calledThrice;
191 | expect(h.shiftNodeUp.firstCall.args[0]).to.equal(nodeToShiftUp);
192 | expect(h.shiftNodeUp.secondCall.args[0]).to.equal(nodeToShiftUp);
193 | expect(h.shiftNodeUp.thirdCall.args[0]).to.equal(nodeToShiftUp);
194 | });
195 | });
196 |
197 | describe('#clear', () => {
198 | it('assigns null to root and [] to parentNodes', () => {
199 | const h = new MaxHeap();
200 | h.push(0, 0);
201 | h.push(15, 2);
202 | h.push(42, 13);
203 |
204 | h.clear();
205 |
206 | expect(h.root).to.equal(null);
207 | expect(h.parentNodes).to.deep.equal([]);
208 | });
209 | });
210 |
211 | describe('#pop', () => {
212 | it('does nothing if heap is empty', () => {
213 | const h = new MaxHeap();
214 | expect(() => {
215 | h.pop();
216 | }).not.to.throw();
217 | });
218 |
219 | it('returns data associated with root', () => {
220 | const h = new MaxHeap();
221 | h.push(42, 15);
222 | h.push(15, 14);
223 | h.push(0, 16);
224 | h.push(100, 100);
225 |
226 | expect(h.pop()).to.equal(100);
227 | expect(h.pop()).to.equal(0);
228 | expect(h.pop()).to.equal(42);
229 | expect(h.pop()).to.equal(15);
230 | });
231 |
232 | it('calls detachRoot', () => {
233 | const h = new MaxHeap();
234 | h.push(42, 15);
235 |
236 | sinon.spy(h, 'detachRoot');
237 |
238 | h.pop();
239 |
240 | expect(h.detachRoot).to.have.been.called;
241 | });
242 |
243 | it('calls restoreRootFromLastInsertedNode with detached root', () => {
244 | const fakeDetachedNode = {};
245 |
246 | const h = new MaxHeap();
247 | h.push(42, 15);
248 |
249 | sinon.stub(h, 'detachRoot').returns(fakeDetachedNode);
250 | sinon.spy(h, 'restoreRootFromLastInsertedNode');
251 |
252 | h.pop();
253 |
254 | expect(h.restoreRootFromLastInsertedNode).to.have.been.calledWith(fakeDetachedNode);
255 | });
256 |
257 | it('calls shiftNodeDown with current heap root', () => {
258 | const h = new MaxHeap();
259 | h.push(42, 15);
260 | h.push(15, 42);
261 | h.push(100, 100);
262 |
263 | const expectedNodeToShiftDown = h.root.right;
264 |
265 | sinon.spy(h, 'shiftNodeDown');
266 |
267 | h.pop();
268 | expect(h.shiftNodeDown).to.have.been.calledWith(expectedNodeToShiftDown);
269 | });
270 | });
271 |
272 | describe('#detachRoot', () => {
273 | let h;
274 |
275 | beforeEach(() => {
276 | h = new MaxHeap();
277 | });
278 |
279 | it('assigns null to this.root', () => {
280 | h.push(42, 15);
281 | h.detachRoot();
282 |
283 | expect(h.root).to.equal(null);
284 | });
285 |
286 | it('removes root from parentNodes', () => {
287 | h.push(42, 15);
288 | h.push(15, 42);
289 |
290 | h.detachRoot();
291 |
292 | expect(h.parentNodes[0].data).to.equal(42);
293 | expect(h.parentNodes[0].priority).to.equal(15);
294 | });
295 |
296 | it('returns detached root', () => {
297 | h.push(42, 15);
298 |
299 | const expected = h.root;
300 | const actual = h.detachRoot();
301 |
302 | expect(actual).to.equal(expected);
303 | });
304 | });
305 |
306 | describe('#restoreRootFromLastInsertedNode', () => {
307 | let h;
308 |
309 | beforeEach(() => {
310 | h = new MaxHeap();
311 |
312 | h.push(42, 15);
313 | h.push(14, 32);
314 | h.push(0, 0);
315 | });
316 |
317 | it('should remove last inserted node and assing it to root', () => {
318 | const lastInsertedNode = h.root.right;
319 | const left = h.root.left;
320 |
321 | const detached = h.detachRoot();
322 | h.restoreRootFromLastInsertedNode(detached);
323 |
324 | expect(h.root).to.equal(lastInsertedNode);
325 | expect(h.root.left).to.equal(left);
326 | expect(left.parent).to.equal(lastInsertedNode);
327 | });
328 |
329 | it('should maintain correct state of parentNodes', () => {
330 | const root = h.root;
331 | const left = h.root.left;
332 | const lastInsertedNode = h.root.right;
333 |
334 | const detached = h.detachRoot();
335 | h.restoreRootFromLastInsertedNode(detached);
336 |
337 | expect(h.parentNodes.indexOf(root)).to.equal(-1);
338 | expect(h.parentNodes[0]).to.equal(lastInsertedNode);
339 | expect(h.parentNodes[1]).to.equal(left);
340 | });
341 |
342 | it('should maintain correct order of parentNodes when right child is moved', () => {
343 | h.push(14,14);
344 | h.push(13,13);
345 | h.push(16,16);
346 | h.push(12,12);
347 |
348 | /**
349 | 32 12
350 | / \ / \
351 | 15 16 - restoreRoot -> 15 16
352 | / \ / \ / \ /
353 | 14 13 0 12 14 13 0
354 | **/
355 |
356 | const detached = h.detachRoot();
357 | h.restoreRootFromLastInsertedNode(detached);
358 |
359 | expect(h.parentNodes.map(n=>n.priority)).to.deep.equal([16,14,13,0]);
360 | });
361 |
362 | });
363 |
364 | describe('#shiftNodeDown', () => {
365 | let h;
366 |
367 | beforeEach(() => {
368 | h = new MaxHeap();
369 |
370 | h.root = new Node(0, 3);
371 | h.root.appendChild(new Node(1, 20));
372 | h.root.appendChild(new Node(2, 7));
373 | h.root.left.appendChild(new Node(3, 5));
374 |
375 | /**
376 | 3 20
377 | / \ / \
378 | 20 7 - shift down -> 5 7
379 | / /
380 | 5 3
381 | **/
382 |
383 | h.parentNodes = [
384 | h.root.left,
385 | h.root.right,
386 | h.root.left.left,
387 | ];
388 | });
389 |
390 | it('shifts node down until heap property is valid', () => {
391 | const newRoot = h.root.left;
392 | const newDeepest = h.root;
393 |
394 | h.shiftNodeDown(h.root);
395 | expect(h.root).to.equal(newRoot);
396 | expect(h.root.left.left).to.equal(newDeepest);
397 | });
398 |
399 | it('maintants parentNodes in correct state', () => {
400 | const correctParentNodesOrderAfterShiftUp = [
401 | h.root.left.left,
402 | h.root.right,
403 | h.root
404 | ]
405 |
406 | h.shiftNodeDown(h.root);
407 |
408 | expect(h.parentNodes[0]).to.equal(correctParentNodesOrderAfterShiftUp[0]);
409 | expect(h.parentNodes[1]).to.equal(correctParentNodesOrderAfterShiftUp[1]);
410 | expect(h.parentNodes[2]).to.equal(correctParentNodesOrderAfterShiftUp[2]);
411 | });
412 |
413 | it('shifts node down in right direction', () => {
414 | h = new MaxHeap();
415 |
416 | let newRoot = new Node(20, 20);
417 | let newDeepest = new Node(1, 1);
418 |
419 | h.root = newDeepest;
420 | h.root.appendChild(new Node(10, 10));
421 | h.root.appendChild(newRoot);
422 | h.root.left.appendChild(new Node(5, 5));
423 | h.root.left.appendChild(new Node(8, 8));
424 | h.root.right.appendChild(new Node(11, 11));
425 | h.root.right.appendChild(new Node(6, 6));
426 |
427 | h.parentNodes = [h.root.left.left, h.root.left.right, h.root.right.left, h.root.right.right];
428 |
429 | /**
430 | 1 20
431 | / \ / \
432 | 10 20 - shiftDown -> 10 11
433 | / \ / \ / \ / \
434 | 5 8 11 6 5 8 1 6
435 | **/
436 |
437 | h.shiftNodeDown(h.root);
438 |
439 | expect(h.root).to.equal(newRoot);
440 | expect(h.root.right.left).to.equal(newDeepest);
441 | expect(h.parentNodes.map(n=>n.priority)).to.deep.equal([5,8,1,6]);
442 | });
443 |
444 | it('calls Node.swapWithParent', () => {
445 | const firstNodeToSwapWith = h.root.left;
446 | sinon.spy(firstNodeToSwapWith, 'swapWithParent');
447 |
448 | h.shiftNodeDown(h.root);
449 |
450 | expect(firstNodeToSwapWith.swapWithParent).to.have.been.calledOnce;
451 | });
452 |
453 | it('calls itself recursively', () => {
454 | const nodeToShiftDown = h.root;
455 | sinon.spy(h, 'shiftNodeDown');
456 |
457 | h.shiftNodeDown(nodeToShiftDown);
458 |
459 | expect(h.shiftNodeDown).to.have.been.calledThrice;
460 | expect(h.shiftNodeDown.firstCall.args[0]).to.equal(nodeToShiftDown);
461 | expect(h.shiftNodeDown.secondCall.args[0]).to.equal(nodeToShiftDown);
462 | expect(h.shiftNodeDown.thirdCall.args[0]).to.equal(nodeToShiftDown);
463 | });
464 | });
465 |
466 | describe('#size', () => {
467 | it('returns current size of heap', () => {
468 | const h = new MaxHeap();
469 | expect(h.size()).to.equal(0);
470 |
471 | h.push(15, 42);
472 | h.push(13, 0);
473 | expect(h.size()).to.equal(2);
474 |
475 | h.push(14, 100);
476 | expect(h.size()).to.equal(3);
477 |
478 | h.pop();
479 | h.pop();
480 | expect(h.size()).to.equal(1);
481 |
482 | h.clear();
483 | expect(h.size()).to.equal(0);
484 | });
485 | });
486 |
487 | describe('#isEmpty', () => {
488 | it('reutrns true if heap is empty', () => {
489 | const h = new MaxHeap();
490 | expect(h.isEmpty()).to.equal(true);
491 |
492 | h.push(100, 500);
493 | expect(h.isEmpty()).to.equal(false);
494 |
495 | h.clear();
496 | expect(h.isEmpty()).to.equal(true);
497 | });
498 | });
499 | });
500 |
--------------------------------------------------------------------------------