diff --git a/awx/ui_next/src/screens/Project/useWsProject.test.jsx b/awx/ui_next/src/screens/Project/useWsProject.test.jsx
new file mode 100644
index 0000000000..fb94ce1c45
--- /dev/null
+++ b/awx/ui_next/src/screens/Project/useWsProject.test.jsx
@@ -0,0 +1,137 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import WS from 'jest-websocket-mock';
+import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
+import useWsProject from './useWsProject';
+
+function TestInner() {
+ return
;
+}
+function Test({ project }) {
+ const synced = useWsProject(project);
+ return ;
+}
+
+describe('useWsProject', () => {
+ let debug;
+ let wrapper;
+ beforeEach(() => {
+ debug = global.console.debug; // eslint-disable-line prefer-destructuring
+ global.console.debug = () => {};
+ });
+
+ afterEach(() => {
+ global.console.debug = debug;
+ });
+
+ test('should return project detail', async () => {
+ const project = { id: 1 };
+ await act(async () => {
+ wrapper = await mountWithContexts();
+ });
+
+ expect(wrapper.find('TestInner').prop('project')).toEqual(project);
+ WS.clean();
+ });
+
+ test('should establish websocket connection', async () => {
+ global.document.cookie = 'csrftoken=abc123';
+ const mockServer = new WS('ws://localhost/websocket/');
+
+ const project = { id: 1 };
+ await act(async () => {
+ wrapper = await mountWithContexts();
+ });
+
+ await mockServer.connected;
+ await expect(mockServer).toReceiveMessage(
+ JSON.stringify({
+ xrftoken: 'abc123',
+ groups: {
+ jobs: ['status_changed'],
+ control: ['limit_reached_1'],
+ },
+ })
+ );
+ WS.clean();
+ });
+
+ test('should update project status', async () => {
+ global.document.cookie = 'csrftoken=abc123';
+ const mockServer = new WS('ws://localhost/websocket/');
+
+ const project = {
+ id: 1,
+ summary_fields: {
+ last_job: {
+ id: 1,
+ status: 'running',
+ finished: null,
+ },
+ },
+ };
+ await act(async () => {
+ wrapper = await mountWithContexts();
+ });
+
+ await mockServer.connected;
+ await expect(mockServer).toReceiveMessage(
+ JSON.stringify({
+ xrftoken: 'abc123',
+ groups: {
+ jobs: ['status_changed'],
+ control: ['limit_reached_1'],
+ },
+ })
+ );
+ expect(
+ wrapper.find('TestInner').prop('project').summary_fields.last_job.status
+ ).toEqual('running');
+
+ await act(async () => {
+ mockServer.send(
+ JSON.stringify({
+ group_name: 'jobs',
+ project_id: 1,
+ status: 'pending',
+ type: 'project_update',
+ unified_job_id: 2,
+ unified_job_template_id: 1,
+ })
+ );
+ });
+ wrapper.update();
+
+ expect(
+ wrapper.find('TestInner').prop('project').summary_fields.last_job
+ ).toEqual({
+ id: 1,
+ status: 'running',
+ finished: null,
+ });
+ // Should not update status to `Pending` if there is a current running or waiting job
+ await act(async () => {
+ mockServer.send(
+ JSON.stringify({
+ group_name: 'jobs',
+ project_id: 1,
+ status: 'successful',
+ type: 'project_update',
+ unified_job_id: 1,
+ unified_job_template_id: 1,
+ finished: '2020-07-02T16:28:31.839071Z',
+ })
+ );
+ });
+ wrapper.update();
+
+ expect(
+ wrapper.find('TestInner').prop('project').summary_fields.last_job
+ ).toEqual({
+ id: 1,
+ status: 'successful',
+ finished: '2020-07-02T16:28:31.839071Z',
+ });
+ WS.clean();
+ });
+});