mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-11 18:37:33 -04:00
Merge branch 'main' into notify-admin-1218
This commit is contained in:
+4
-4
@@ -133,7 +133,7 @@
|
||||
"filename": ".github/workflows/checks.yml",
|
||||
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
|
||||
"is_verified": false,
|
||||
"line_number": 68,
|
||||
"line_number": 65,
|
||||
"is_secret": false
|
||||
},
|
||||
{
|
||||
@@ -141,7 +141,7 @@
|
||||
"filename": ".github/workflows/checks.yml",
|
||||
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
|
||||
"is_verified": false,
|
||||
"line_number": 103,
|
||||
"line_number": 99,
|
||||
"is_secret": false
|
||||
}
|
||||
],
|
||||
@@ -535,7 +535,7 @@
|
||||
"filename": "tests/app/main/views/test_accept_invite.py",
|
||||
"hashed_secret": "07f0a6c13923fc3b5f0c57ffa2d29b715eb80d71",
|
||||
"is_verified": false,
|
||||
"line_number": 626,
|
||||
"line_number": 643,
|
||||
"is_secret": false
|
||||
}
|
||||
],
|
||||
@@ -692,5 +692,5 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"generated_at": "2024-08-07T14:36:40Z"
|
||||
"generated_at": "2024-08-08T19:45:34Z"
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ jobs:
|
||||
run: poetry run isort --check-only ./app ./tests
|
||||
- name: Check dead code
|
||||
run: make dead-code
|
||||
- name: Run js lint
|
||||
run: npm run lint
|
||||
- name: Run js tests
|
||||
run: npm test
|
||||
- name: Run py tests with coverage
|
||||
|
||||
+1
-4
@@ -18,7 +18,6 @@ from flask import (
|
||||
)
|
||||
from flask.globals import request_ctx
|
||||
from flask_login import LoginManager, current_user
|
||||
from flask_socketio import SocketIO
|
||||
from flask_talisman import Talisman
|
||||
from flask_wtf import CSRFProtect
|
||||
from flask_wtf.csrf import CSRFError
|
||||
@@ -119,8 +118,6 @@ from notifications_utils.recipients import format_phone_number_human_readable
|
||||
login_manager = LoginManager()
|
||||
csrf = CSRFProtect()
|
||||
talisman = Talisman()
|
||||
socketio = SocketIO()
|
||||
|
||||
|
||||
# The current service attached to the request stack.
|
||||
current_service = LocalProxy(partial(getattr, request_ctx, "service"))
|
||||
@@ -177,7 +174,7 @@ def create_app(application):
|
||||
|
||||
init_govuk_frontend(application)
|
||||
init_jinja(application)
|
||||
socketio.init_app(application, cors_allowed_origins=["http://localhost:6012"])
|
||||
|
||||
|
||||
for client in (
|
||||
csrf,
|
||||
|
||||
@@ -185,41 +185,41 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var socket = io("/services");
|
||||
var eventType = type === 'service' ? 'fetch_daily_stats' : 'fetch_daily_stats_by_user';
|
||||
var socketConnect = type === 'service' ? 'daily_stats_update' : 'daily_stats_by_user_update';
|
||||
var daily_stats = activityChartContainer.getAttribute('data-daily-stats');
|
||||
var daily_stats_by_user = activityChartContainer.getAttribute('data-daily_stats_by_user');
|
||||
|
||||
socket.on('connect', function () {
|
||||
socket.emit(eventType);
|
||||
});
|
||||
try {
|
||||
// Choose the correct JSON string based on the type ('service' or 'user'),
|
||||
// replace single quotes with double quotes to ensure valid JSON format,
|
||||
// then parse the JSON string into a JavaScript object.
|
||||
var statsJson = type === 'service' ? daily_stats : daily_stats_by_user;
|
||||
statsJson = statsJson.replace(/'/g, '"');
|
||||
data = JSON.parse(statsJson);
|
||||
} catch (error) {
|
||||
console.error('Error parsing JSON data:', error);
|
||||
return;
|
||||
}
|
||||
var labels = [];
|
||||
var deliveredData = [];
|
||||
var failedData = [];
|
||||
|
||||
socket.on('connect_error', function(error) {
|
||||
console.error('WebSocket connection error:', error);
|
||||
});
|
||||
|
||||
socket.on(socketConnect, function(data) {
|
||||
|
||||
var labels = [];
|
||||
var deliveredData = [];
|
||||
var failedData = [];
|
||||
|
||||
for (var dateString in data) {
|
||||
// Parse the date string (assuming format YYYY-MM-DD)
|
||||
for (var dateString in data) {
|
||||
if (data.hasOwnProperty(dateString)) {
|
||||
const dateParts = dateString.split('-');
|
||||
const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`; // Format to MM/DD/YY
|
||||
const formattedDate = `${dateParts[1]}/${dateParts[2]}/${dateParts[0].slice(2)}`;
|
||||
|
||||
labels.push(formattedDate);
|
||||
deliveredData.push(data[dateString].sms.delivered);
|
||||
failedData.push(data[dateString].sms.failure);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
createChart('#weeklyChart', labels, deliveredData, failedData);
|
||||
createTable('weeklyTable', 'activityChart', labels, deliveredData, failedData);
|
||||
});
|
||||
|
||||
socket.on('error', function(data) {
|
||||
console.log('Error:', data);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error creating chart or table:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDropdownChange = function(event) {
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
(function (window) {
|
||||
|
||||
function initializeChartAndSocket() {
|
||||
var ctx = document.getElementById('myChart');
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
var myBarChart = new Chart(ctx.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Delivered',
|
||||
data: [],
|
||||
backgroundColor: '#0076d6',
|
||||
stack: 'Stack 0'
|
||||
},
|
||||
]
|
||||
},
|
||||
options: {
|
||||
animation: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var socket = io();
|
||||
|
||||
socket.on('connect', function() {
|
||||
socket.emit('fetch_daily_stats_by_user');
|
||||
});
|
||||
|
||||
socket.on('daily_stats_by_user_update', function(data) {
|
||||
// console.log('Data received:', data);
|
||||
var labels = [];
|
||||
var deliveredData = [];
|
||||
var failedData = [];
|
||||
|
||||
for (var date in data) {
|
||||
labels.push(date);
|
||||
deliveredData.push(data[date].sms.delivered);
|
||||
|
||||
}
|
||||
|
||||
myBarChart.data.labels = labels;
|
||||
myBarChart.data.datasets[0].data = deliveredData;
|
||||
myBarChart.update();
|
||||
});
|
||||
|
||||
socket.on('error', function(data) {
|
||||
// console.log('Error:', data);
|
||||
});
|
||||
|
||||
var sevenDaysButton = document.getElementById('sevenDaysButton');
|
||||
if (sevenDaysButton) {
|
||||
sevenDaysButton.addEventListener('click', function() {
|
||||
socket.emit('fetch_daily_stats_by_user');
|
||||
// console.log('clicked');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initializeChartAndSocket);
|
||||
|
||||
})(window);
|
||||
+22
-34
@@ -5,7 +5,6 @@ from itertools import groupby
|
||||
|
||||
from flask import Response, abort, jsonify, render_template, request, session, url_for
|
||||
from flask_login import current_user
|
||||
from flask_socketio import emit
|
||||
from werkzeug.utils import redirect
|
||||
|
||||
from app import (
|
||||
@@ -13,7 +12,6 @@ from app import (
|
||||
current_service,
|
||||
job_api_client,
|
||||
service_api_client,
|
||||
socketio,
|
||||
template_statistics_client,
|
||||
)
|
||||
from app.formatters import format_date_numeric, format_datetime_numeric, get_time_left
|
||||
@@ -32,38 +30,6 @@ from app.utils.user import user_has_permissions
|
||||
from notifications_utils.recipients import format_phone_number_human_readable
|
||||
|
||||
|
||||
@socketio.on("fetch_daily_stats", namespace="/services")
|
||||
def handle_fetch_daily_stats():
|
||||
service_id = session.get("service_id")
|
||||
if service_id:
|
||||
date_range = get_stats_date_range()
|
||||
daily_stats = service_api_client.get_service_notification_statistics_by_day(
|
||||
service_id, start_date=date_range["start_date"], days=date_range["days"]
|
||||
)
|
||||
emit("daily_stats_update", daily_stats)
|
||||
else:
|
||||
emit("error", {"error": "No service_id provided"})
|
||||
|
||||
|
||||
@socketio.on("fetch_daily_stats_by_user", namespace="/services")
|
||||
def handle_fetch_daily_stats_by_user():
|
||||
service_id = session.get("service_id")
|
||||
user_id = session.get("user_id")
|
||||
if service_id and user_id:
|
||||
date_range = get_stats_date_range()
|
||||
daily_stats_by_user = (
|
||||
service_api_client.get_user_service_notification_statistics_by_day(
|
||||
service_id,
|
||||
user_id,
|
||||
start_date=date_range["start_date"],
|
||||
days=date_range["days"],
|
||||
)
|
||||
)
|
||||
emit("daily_stats_by_user_update", daily_stats_by_user)
|
||||
else:
|
||||
emit("error", {"error": "No service_id or user_id provided"})
|
||||
|
||||
|
||||
@main.route("/services/<uuid:service_id>/dashboard")
|
||||
@user_has_permissions("view_activity", "send_messages")
|
||||
def old_service_dashboard(service_id):
|
||||
@@ -87,12 +53,17 @@ def service_dashboard(service_id):
|
||||
free_sms_allowance = billing_api_client.get_free_sms_fragment_limit_for_year(
|
||||
current_service.id,
|
||||
)
|
||||
date_range = get_stats_date_range()
|
||||
usage_data = get_annual_usage_breakdown(yearly_usage, free_sms_allowance)
|
||||
sms_sent = usage_data["sms_sent"]
|
||||
sms_allowance_remaining = usage_data["sms_allowance_remaining"]
|
||||
|
||||
job_response = job_api_client.get_jobs(service_id)["data"]
|
||||
service_data_retention_days = 7
|
||||
daily_stats = get_daily_stats(service_id, date_range)
|
||||
daily_stats_by_user = get_daily_stats_by_user(
|
||||
service_id, current_user.id, date_range
|
||||
)
|
||||
|
||||
jobs = [
|
||||
{
|
||||
@@ -123,6 +94,23 @@ def service_dashboard(service_id):
|
||||
service_data_retention_days=service_data_retention_days,
|
||||
sms_sent=sms_sent,
|
||||
sms_allowance_remaining=sms_allowance_remaining,
|
||||
daily_stats=daily_stats,
|
||||
daily_stats_by_user=daily_stats_by_user,
|
||||
)
|
||||
|
||||
|
||||
def get_daily_stats(service_id, date_range):
|
||||
return service_api_client.get_service_notification_statistics_by_day(
|
||||
service_id, start_date=date_range["start_date"], days=date_range["days"]
|
||||
)
|
||||
|
||||
|
||||
def get_daily_stats_by_user(service_id, user_id, date_range):
|
||||
return service_api_client.get_user_service_notification_statistics_by_day(
|
||||
service_id,
|
||||
user_id,
|
||||
start_date=date_range["start_date"],
|
||||
days=date_range["days"],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<h2 class="line-height-sans-2 margin-bottom-0 margin-top-4">
|
||||
Activity snapshot
|
||||
</h2>
|
||||
<div id="activityChartContainer">
|
||||
<div id="activityChartContainer" data-daily-stats="{{ daily_stats }}" data-daily_stats_by_user="{{ daily_stats_by_user }}">
|
||||
<form class="usa-form">
|
||||
<label class="usa-label" for="options">Account</label>
|
||||
<select class="usa-select margin-bottom-2" name="options" id="options">
|
||||
|
||||
+51
-178
@@ -1,15 +1,10 @@
|
||||
// GULPFILE
|
||||
// - - - - - - - - - - - - - - -
|
||||
// This file processes all of the assets in the "src" folder
|
||||
// and outputs the finished files in the "dist" folder.
|
||||
|
||||
// 1. LIBRARIES
|
||||
// - - - - - - - - - - - - - - -
|
||||
const { src, pipe, dest, series, parallel, watch } = require('gulp');
|
||||
const rollupPluginCommonjs = require('rollup-plugin-commonjs');
|
||||
const rollupPluginNodeResolve = require('rollup-plugin-node-resolve');
|
||||
const streamqueue = require('streamqueue');
|
||||
const stylish = require('jshint-stylish');
|
||||
const { src, dest, series } = require('gulp');
|
||||
const rollup = require('@rollup/stream');
|
||||
const rollupPluginCommonjs = require('@rollup/plugin-commonjs');
|
||||
const rollupPluginNodeResolve = require('@rollup/plugin-node-resolve');
|
||||
const source = require('vinyl-source-stream');
|
||||
const buffer = require('vinyl-buffer');
|
||||
const gulpMerge = require('gulp-merge');
|
||||
const uswds = require("@uswds/compile");
|
||||
|
||||
const plugins = {};
|
||||
@@ -19,11 +14,8 @@ plugins.cleanCSS = require('gulp-clean-css');
|
||||
plugins.concat = require('gulp-concat');
|
||||
plugins.jshint = require('gulp-jshint');
|
||||
plugins.prettyerror = require('gulp-prettyerror');
|
||||
plugins.rollup = require('gulp-better-rollup')
|
||||
plugins.uglify = require('gulp-uglify');
|
||||
|
||||
// 2. CONFIGURATION
|
||||
// - - - - - - - - - - - - - - -
|
||||
const paths = {
|
||||
src: 'app/assets/',
|
||||
dist: 'app/static/',
|
||||
@@ -31,63 +23,25 @@ const paths = {
|
||||
toolkit: 'node_modules/govuk_frontend_toolkit/',
|
||||
govuk_frontend: 'node_modules/govuk-frontend/'
|
||||
};
|
||||
// Rewrite /static prefix for URLs in CSS files
|
||||
let staticPathMatcher = new RegExp('^\/static\/');
|
||||
if (process.env.NOTIFY_ENVIRONMENT == 'development') { // pass through if on development
|
||||
staticPathMatcher = url => url;
|
||||
}
|
||||
|
||||
// 3. TASKS
|
||||
// - - - - - - - - - - - - - - -
|
||||
|
||||
// Move GOV.UK template resources
|
||||
|
||||
const copy = {
|
||||
error_pages: () => {
|
||||
return src(paths.src + 'error_pages/**/*')
|
||||
.pipe(dest(paths.dist + 'error_pages/'))
|
||||
},
|
||||
fonts: () => {
|
||||
return src(paths.src + 'fonts/**/*')
|
||||
.pipe(dest(paths.dist + 'fonts/'));
|
||||
},
|
||||
gtm: () => {
|
||||
return src(paths.src + 'js/gtm_head.js')
|
||||
.pipe(dest(paths.dist + 'js/'));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const javascripts = () => {
|
||||
// JS from third-party sources
|
||||
// We assume none of it will need to pass through Babel
|
||||
const vendored = src(paths.src + 'javascripts/modules/all.mjs')
|
||||
// Use Rollup to combine all JS in JS module format into a Immediately Invoked Function
|
||||
// Expression (IIFE) to:
|
||||
// - deliver it in one bundle
|
||||
// - allow it to run in browsers without support for JS Modules
|
||||
.pipe(plugins.rollup(
|
||||
{
|
||||
plugins: [
|
||||
// determine module entry points from either 'module' or 'main' fields in package.json
|
||||
rollupPluginNodeResolve({
|
||||
mainFields: ['module', 'main']
|
||||
}),
|
||||
// gulp rollup runs on nodeJS so reads modules in commonJS format
|
||||
// this adds node_modules to the require path so it can find the GOVUK Frontend modules
|
||||
rollupPluginCommonjs({
|
||||
include: 'node_modules/**'
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
format: 'iife',
|
||||
name: 'GOVUK'
|
||||
}
|
||||
))
|
||||
// return a stream which pipes these files before the JS modules bundle
|
||||
const vendored = rollup({
|
||||
input: paths.src + 'javascripts/modules/all.mjs',
|
||||
plugins: [
|
||||
rollupPluginNodeResolve({
|
||||
mainFields: ['module', 'main']
|
||||
}),
|
||||
rollupPluginCommonjs({
|
||||
include: 'node_modules/**'
|
||||
})
|
||||
],
|
||||
output: {
|
||||
format: 'iife',
|
||||
name: 'GOVUK'
|
||||
}
|
||||
})
|
||||
.pipe(source('all.mjs'))
|
||||
.pipe(buffer())
|
||||
.pipe(plugins.addSrc.prepend([
|
||||
paths.npm + 'hogan.js/dist/hogan-3.0.2.js',
|
||||
paths.npm + 'jquery/dist/jquery.min.js',
|
||||
@@ -95,11 +49,9 @@ const javascripts = () => {
|
||||
paths.npm + 'timeago/jquery.timeago.js',
|
||||
paths.npm + 'textarea-caret/index.js',
|
||||
paths.npm + 'cbor-js/cbor.js',
|
||||
paths.npm + 'socket.io-client/dist/socket.io.min.js',
|
||||
paths.npm + 'd3/dist/d3.min.js'
|
||||
]));
|
||||
|
||||
// JS local to this application
|
||||
const local = src([
|
||||
paths.toolkit + 'javascripts/govuk/modules.js',
|
||||
paths.toolkit + 'javascripts/govuk/show-hide-content.js',
|
||||
@@ -124,128 +76,49 @@ const javascripts = () => {
|
||||
paths.src + 'javascripts/timeoutPopup.js',
|
||||
paths.src + 'javascripts/date.js',
|
||||
paths.src + 'javascripts/loginAlert.js',
|
||||
paths.src + 'javascripts/main.js',
|
||||
paths.src + 'javascripts/totalMessagesChart.js',
|
||||
paths.src + 'javascripts/activityChart.js',
|
||||
paths.src + 'javascripts/main.js',
|
||||
])
|
||||
.pipe(plugins.prettyerror())
|
||||
.pipe(plugins.babel({
|
||||
presets: ['@babel/preset-env']
|
||||
}));
|
||||
|
||||
// return single stream of all vinyl objects piped from the end of the vendored stream, then
|
||||
// those from the end of the local stream
|
||||
return streamqueue({ objectMode: true }, vendored, local)
|
||||
return gulpMerge(vendored, local)
|
||||
.pipe(plugins.uglify())
|
||||
.pipe(plugins.concat('all.js'))
|
||||
.pipe(dest(paths.dist + 'javascripts/'))
|
||||
.pipe(dest(paths.dist + 'javascripts/'));
|
||||
};
|
||||
|
||||
|
||||
// Copy images
|
||||
|
||||
const images = () => {
|
||||
return src([
|
||||
paths.toolkit + 'images/**/*',
|
||||
paths.govuk_frontend + 'assets/images/**/*',
|
||||
paths.src + 'images/**/*',
|
||||
paths.src + 'img/**/*',
|
||||
], {encoding: false})
|
||||
.pipe(dest(paths.dist + 'images/'))
|
||||
// Task to copy `gtm_head.js`
|
||||
const copyGtmHead = () => {
|
||||
return src(paths.src + 'js/gtm_head.js')
|
||||
.pipe(dest(paths.dist + 'js/'));
|
||||
};
|
||||
|
||||
|
||||
const watchFiles = {
|
||||
javascripts: (cb) => {
|
||||
watch([paths.src + 'javascripts/**/*'], javascripts);
|
||||
cb();
|
||||
},
|
||||
images: (cb) => {
|
||||
watch([paths.src + 'images/**/*'], images);
|
||||
cb();
|
||||
},
|
||||
uswds: (cb) => {
|
||||
watch([paths.src + 'sass/**/*'], uswds.watch);
|
||||
cb();
|
||||
},
|
||||
self: (cb) => {
|
||||
watch(['gulpfile.js'], defaultTask);
|
||||
cb();
|
||||
}
|
||||
// Task to copy images
|
||||
const copyImages = () => {
|
||||
return src(paths.src + 'images/**/*')
|
||||
.pipe(dest(paths.dist + 'images/'));
|
||||
};
|
||||
|
||||
|
||||
const lint = {
|
||||
'js': (cb) => {
|
||||
return src(
|
||||
paths.src + 'javascripts/**/*.js'
|
||||
)
|
||||
.pipe(plugins.jshint())
|
||||
.pipe(plugins.jshint.reporter(stylish))
|
||||
.pipe(plugins.jshint.reporter('fail'))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Default: compile everything
|
||||
const defaultTask = parallel(
|
||||
parallel(
|
||||
copy.fonts,
|
||||
images
|
||||
),
|
||||
series(
|
||||
copy.error_pages,
|
||||
series(
|
||||
javascripts
|
||||
),
|
||||
uswds.compile,
|
||||
uswds.copyAssets,
|
||||
copy.gtm
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// Watch for changes and re-run tasks
|
||||
const watchForChanges = parallel(
|
||||
watchFiles.javascripts,
|
||||
watchFiles.images,
|
||||
watchFiles.self
|
||||
);
|
||||
|
||||
|
||||
exports.default = defaultTask;
|
||||
|
||||
exports.lint = series(lint.js);
|
||||
|
||||
// Optional: recompile on changes
|
||||
exports.watch = series(defaultTask, watchForChanges);
|
||||
|
||||
|
||||
// 3. Compile USWDS
|
||||
|
||||
/**
|
||||
* USWDS version
|
||||
* Set the major version of USWDS you're using
|
||||
* (Current options are the numbers 2 or 3)
|
||||
*/
|
||||
// Configure USWDS paths
|
||||
uswds.settings.version = 3;
|
||||
uswds.paths.dist.css = paths.dist + 'css';
|
||||
uswds.paths.dist.js = paths.dist + 'js';
|
||||
uswds.paths.dist.img = paths.dist + 'img';
|
||||
uswds.paths.dist.fonts = paths.dist + 'fonts';
|
||||
uswds.paths.dist.theme = paths.src + 'sass/uswds';
|
||||
|
||||
/**
|
||||
* Path settings
|
||||
* Set as many as you need
|
||||
*/
|
||||
uswds.paths.dist.css = './app/static/css';
|
||||
uswds.paths.dist.js = './app/static/js';
|
||||
uswds.paths.dist.img = './app/static/img';
|
||||
uswds.paths.dist.fonts = './app/static/fonts';
|
||||
uswds.paths.dist.theme = './app/assets/sass/uswds';
|
||||
// Task to compile USWDS styles
|
||||
const styles = async () => {
|
||||
await uswds.compile();
|
||||
};
|
||||
|
||||
/**
|
||||
* Exports
|
||||
* Add as many as you need
|
||||
*/
|
||||
exports.init = uswds.init;
|
||||
exports.compile = uswds.compile;
|
||||
exports.copyAll = uswds.copyAll;
|
||||
exports.watch = uswds.watch;
|
||||
exports.copyAssets = uswds.copyAssets;
|
||||
// Task to copy USWDS assets
|
||||
const copyAssets = async () => {
|
||||
await uswds.copyAssets();
|
||||
};
|
||||
|
||||
exports.default = series(styles, javascripts, copyGtmHead, copyImages, copyAssets);
|
||||
|
||||
Generated
+1206
-513
File diff suppressed because it is too large
Load Diff
+15
-17
@@ -25,49 +25,47 @@
|
||||
"graceful-fs": "^4.2.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rollup/plugin-commonjs": "^26.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@rollup/stream": "^3.0.1",
|
||||
"@uswds/uswds": "^3.8.1",
|
||||
"cbor-js": "0.1.0",
|
||||
"d3": "^7.9.0",
|
||||
"govuk_frontend_toolkit": "^9.0.1",
|
||||
"govuk-frontend": "2.13.0",
|
||||
"gulp-merge": "^0.1.1",
|
||||
"hogan": "1.0.2",
|
||||
"jquery": "3.7.1",
|
||||
"morphdom": "^2.7.3",
|
||||
"morphdom": "^2.7.4",
|
||||
"python": "^0.0.4",
|
||||
"query-command-supported": "1.0.0",
|
||||
"sass-embedded": "^1.77.5",
|
||||
"socket.io-client": "^4.2.0",
|
||||
"sass-embedded": "^1.77.8",
|
||||
"textarea-caret": "3.1.0",
|
||||
"timeago": "1.6.7"
|
||||
"timeago": "1.6.7",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.7",
|
||||
"@babel/preset-env": "^7.24.7",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@uswds/compile": "^1.1.0",
|
||||
"babel-jest": "^29.7.0",
|
||||
"better-npm-audit": "^3.7.3",
|
||||
"gulp": "^5.0.0",
|
||||
"gulp-add-src": "^1.0.0",
|
||||
"gulp-babel": "8.0.0",
|
||||
"gulp-better-rollup": "4.0.1",
|
||||
"gulp-clean-css": "4.3.0",
|
||||
"gulp-concat": "2.6.1",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-include": "2.4.1",
|
||||
"gulp-jshint": "2.1.0",
|
||||
"gulp-prettyerror": "2.0.0",
|
||||
"gulp-uglify": "3.0.2",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-each": "^29.2.1",
|
||||
"jest-environment-jsdom": "^29.2.2",
|
||||
"jshint": "2.13.6",
|
||||
"jshint-stylish": "2.2.1",
|
||||
"rollup": "1.32.1",
|
||||
"rollup": "^4.20.0",
|
||||
"rollup-plugin-commonjs": "10.1.0",
|
||||
"rollup-plugin-node-resolve": "5.2.0",
|
||||
"streamqueue": "1.1.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sass-embedded-linux-x64": "^1.77.8"
|
||||
"rollup-plugin-node-resolve": "5.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10
-116
@@ -85,17 +85,6 @@ charset-normalizer = ["charset-normalizer"]
|
||||
html5lib = ["html5lib"]
|
||||
lxml = ["lxml"]
|
||||
|
||||
[[package]]
|
||||
name = "bidict"
|
||||
version = "0.23.1"
|
||||
description = "The bidirectional mapping library for Python."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"},
|
||||
{file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.4.2"
|
||||
@@ -893,24 +882,6 @@ redis = ">=2.7.6"
|
||||
dev = ["coverage", "pre-commit", "pytest", "pytest-mock"]
|
||||
tests = ["coverage", "pytest", "pytest-mock"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-socketio"
|
||||
version = "5.3.6"
|
||||
description = "Socket.IO integration for Flask applications"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "Flask-SocketIO-5.3.6.tar.gz", hash = "sha256:bb8f9f9123ef47632f5ce57a33514b0c0023ec3696b2384457f0fcaa5b70501c"},
|
||||
{file = "Flask_SocketIO-5.3.6-py3-none-any.whl", hash = "sha256:9e62d2131842878ae6bfdd7067dfc3be397c1f2b117ab1dc74e6fe74aad7a579"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Flask = ">=0.9"
|
||||
python-socketio = ">=5.0.2"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-talisman"
|
||||
version = "1.1.0"
|
||||
@@ -1059,17 +1030,6 @@ setproctitle = ["setproctitle"]
|
||||
testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
|
||||
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "html5lib"
|
||||
version = "1.1"
|
||||
@@ -1325,9 +1285,13 @@ files = [
|
||||
{file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"},
|
||||
{file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"},
|
||||
@@ -1662,7 +1626,6 @@ files = [
|
||||
{file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
|
||||
{file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
|
||||
{file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
|
||||
{file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
|
||||
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
|
||||
]
|
||||
|
||||
@@ -2406,25 +2369,6 @@ files = [
|
||||
[package.extras]
|
||||
cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-engineio"
|
||||
version = "4.9.1"
|
||||
description = "Engine.IO server and client for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "python_engineio-4.9.1-py3-none-any.whl", hash = "sha256:f995e702b21f6b9ebde4e2000cd2ad0112ba0e5116ec8d22fe3515e76ba9dddd"},
|
||||
{file = "python_engineio-4.9.1.tar.gz", hash = "sha256:7631cf5563086076611e494c643b3fa93dd3a854634b5488be0bba0ef9b99709"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
simple-websocket = ">=0.10.0"
|
||||
|
||||
[package.extras]
|
||||
asyncio-client = ["aiohttp (>=3.4)"]
|
||||
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "python-json-logger"
|
||||
version = "2.0.7"
|
||||
@@ -2453,26 +2397,6 @@ text-unidecode = ">=1.3"
|
||||
[package.extras]
|
||||
unidecode = ["Unidecode (>=1.1.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-socketio"
|
||||
version = "5.11.3"
|
||||
description = "Socket.IO server and client for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python_socketio-5.11.3-py3-none-any.whl", hash = "sha256:2a923a831ff70664b7c502df093c423eb6aa93c1ce68b8319e840227a26d8b69"},
|
||||
{file = "python_socketio-5.11.3.tar.gz", hash = "sha256:194af8cdbb7b0768c2e807ba76c7abc288eb5bb85559b7cddee51a6bc7a65737"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
bidict = ">=0.21.0"
|
||||
python-engineio = ">=4.8.0"
|
||||
|
||||
[package.extras]
|
||||
asyncio-client = ["aiohttp (>=3.4)"]
|
||||
client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2024.1"
|
||||
@@ -2509,6 +2433,7 @@ files = [
|
||||
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
|
||||
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
|
||||
@@ -2563,17 +2488,17 @@ toml = ["tomli (>=2.0.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "redis"
|
||||
version = "5.0.7"
|
||||
version = "5.0.8"
|
||||
description = "Python client for Redis database and key-value store"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"},
|
||||
{file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"},
|
||||
{file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"},
|
||||
{file = "redis-5.0.8.tar.gz", hash = "sha256:0c5b10d387568dfe0698c6fad6615750c24170e548ca2deac10c649d463e9870"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
hiredis = ["hiredis (>=1.0.0)"]
|
||||
hiredis = ["hiredis (>1.0.0)"]
|
||||
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||
|
||||
[[package]]
|
||||
@@ -2822,23 +2747,6 @@ numpy = ">=1.14,<3"
|
||||
docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"]
|
||||
test = ["pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "simple-websocket"
|
||||
version = "1.0.0"
|
||||
description = "Simple WebSocket server and client for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "simple-websocket-1.0.0.tar.gz", hash = "sha256:17d2c72f4a2bd85174a97e3e4c88b01c40c3f81b7b648b0cc3ce1305968928c8"},
|
||||
{file = "simple_websocket-1.0.0-py3-none-any.whl", hash = "sha256:1d5bf585e415eaa2083e2bcf02a3ecf91f9712e7b3e6b9fa0b461ad04e0837bc"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
wsproto = "*"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
@@ -3016,20 +2924,6 @@ MarkupSafe = ">=2.1.1"
|
||||
[package.extras]
|
||||
watchdog = ["watchdog (>=2.3)"]
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.2.0"
|
||||
description = "WebSockets state-machine based protocol implementation"
|
||||
optional = false
|
||||
python-versions = ">=3.7.0"
|
||||
files = [
|
||||
{file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"},
|
||||
{file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
h11 = ">=0.9.0,<1"
|
||||
|
||||
[[package]]
|
||||
name = "wtforms"
|
||||
version = "3.1.2"
|
||||
@@ -3088,4 +2982,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12.2"
|
||||
content-hash = "9d6309a76755b2639d787f99944c1ead0bd93ab9f2f13208f88c51cecb5e0081"
|
||||
content-hash = "2c1efc5e8d38c709aec2bc3aa39f96c82a58df13bcbf1912b27c4c26b7b4fc9d"
|
||||
|
||||
+1
-2
@@ -52,7 +52,7 @@ ordered-set = "^4.1.0"
|
||||
phonenumbers = "^8.13.40"
|
||||
pycparser = "^2.22"
|
||||
python-json-logger = "^2.0.7"
|
||||
redis = "^5.0.7"
|
||||
redis = "^5.0.8"
|
||||
regex = "^2024.7.24"
|
||||
s3transfer = "^0.10.2"
|
||||
shapely = "^2.0.5"
|
||||
@@ -68,7 +68,6 @@ requests = "^2.32.3"
|
||||
six = "^1.16.0"
|
||||
urllib3 = "^2.2.2"
|
||||
webencodings = "^0.5.1"
|
||||
flask-socketio = "^5.3.6"
|
||||
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
||||
@@ -300,9 +300,26 @@ def test_accepting_invite_removes_invite_from_session(
|
||||
|
||||
client_request.login(user)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
date_range = {"start_date": "2024-01-01", "days": 7}
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
"sms": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.accept_invite",
|
||||
|
||||
@@ -3,11 +3,9 @@ import json
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from flask import Flask, url_for
|
||||
from flask_socketio import SocketIO, SocketIOTestClient
|
||||
from flask import url_for
|
||||
from freezegun import freeze_time
|
||||
|
||||
from app import create_app
|
||||
from app.main.views.dashboard import (
|
||||
aggregate_notifications_stats,
|
||||
aggregate_status_types,
|
||||
@@ -15,8 +13,6 @@ from app.main.views.dashboard import (
|
||||
format_monthly_stats_to_list,
|
||||
get_dashboard_totals,
|
||||
get_tuples_of_financial_years,
|
||||
handle_fetch_daily_stats,
|
||||
handle_fetch_daily_stats_by_user,
|
||||
)
|
||||
from tests import (
|
||||
organization_json,
|
||||
@@ -27,8 +23,6 @@ from tests import (
|
||||
from tests.conftest import (
|
||||
ORGANISATION_ID,
|
||||
SERVICE_ONE_ID,
|
||||
SERVICE_TWO_ID,
|
||||
USER_ONE_ID,
|
||||
create_active_caseworking_user,
|
||||
create_active_user_view_permissions,
|
||||
normalize_spaces,
|
||||
@@ -163,6 +157,22 @@ stub_template_stats = [
|
||||
},
|
||||
]
|
||||
|
||||
date_range = {"start_date": "2024-01-01", "days": 7}
|
||||
|
||||
mock_daily_stats = {
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
}
|
||||
|
||||
mock_daily_stats_by_user = {
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
"sms": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"user",
|
||||
@@ -222,8 +232,12 @@ def test_get_started(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -252,8 +266,12 @@ def test_get_started_is_hidden_once_templates_exist(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -279,8 +297,12 @@ def test_inbound_messages_not_visible_to_service_without_permissions(
|
||||
service_one["permissions"] = []
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -305,6 +327,14 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_messages(
|
||||
mock_get_inbound_sms_summary,
|
||||
):
|
||||
service_one["permissions"] = ["inbound_sms"]
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
service_id=SERVICE_ONE_ID,
|
||||
@@ -333,6 +363,14 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_no_messages(
|
||||
mock_get_inbound_sms_summary_with_no_messages,
|
||||
):
|
||||
service_one["permissions"] = ["inbound_sms"]
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
service_id=SERVICE_ONE_ID,
|
||||
@@ -580,8 +618,12 @@ def test_should_show_recent_templates_on_dashboard(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -637,8 +679,12 @@ def test_should_not_show_recent_templates_on_dashboard_if_only_one_template_used
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
main = page.select_one("main").text
|
||||
@@ -792,8 +838,12 @@ def test_should_show_upcoming_jobs_on_dashboard(
|
||||
mock_get_inbound_sms_summary,
|
||||
):
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -834,6 +884,14 @@ def test_should_not_show_upcoming_jobs_on_dashboard_if_count_is_0(
|
||||
},
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
service_id=SERVICE_ONE_ID,
|
||||
@@ -857,8 +915,12 @@ def test_should_not_show_upcoming_jobs_on_dashboard_if_service_has_no_jobs(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
@@ -942,6 +1004,14 @@ def test_should_not_show_jobs_on_dashboard_for_users_with_uploads_page(
|
||||
mock_get_free_sms_fragment_limit,
|
||||
mock_get_inbound_sms_summary,
|
||||
):
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get(
|
||||
"main.service_dashboard",
|
||||
service_id=SERVICE_ONE_ID,
|
||||
@@ -1203,8 +1273,12 @@ def test_menu_send_messages(
|
||||
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = _test_dashboard_menu(
|
||||
client_request,
|
||||
@@ -1240,8 +1314,12 @@ def test_menu_manage_service(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = _test_dashboard_menu(
|
||||
client_request,
|
||||
@@ -1277,8 +1355,12 @@ def test_menu_main_settings(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = _test_settings_menu(
|
||||
client_request,
|
||||
@@ -1313,8 +1395,12 @@ def test_menu_manage_api_keys(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = _test_dashboard_menu(
|
||||
client_request,
|
||||
@@ -1353,8 +1439,12 @@ def test_menu_all_services_for_platform_admin_user(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = _test_dashboard_menu(
|
||||
client_request, mocker, platform_admin_user, service_one, []
|
||||
@@ -1392,8 +1482,12 @@ def test_route_for_service_permissions(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
validate_route_permission(
|
||||
mocker,
|
||||
@@ -1537,8 +1631,12 @@ def test_org_breadcrumbs_do_not_show_if_service_has_no_org(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
|
||||
@@ -1604,8 +1702,12 @@ def test_org_breadcrumbs_show_if_user_is_a_member_of_the_services_org(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
@@ -1639,10 +1741,13 @@ def test_org_breadcrumbs_do_not_show_if_user_is_a_member_of_the_services_org_but
|
||||
mocker.patch("app.models.service.Organization")
|
||||
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
@@ -1679,8 +1784,12 @@ def test_org_breadcrumbs_show_if_user_is_platform_admin(
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
client_request.login(platform_admin_user, service_one_json)
|
||||
@@ -1715,9 +1824,14 @@ def test_breadcrumb_shows_if_service_is_suspended(
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
|
||||
assert "Suspended" in page.select_one(".navigation-service-name").text
|
||||
@@ -1748,8 +1862,12 @@ def test_service_dashboard_shows_usage(
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
service_one["permissions"] = permissions
|
||||
@@ -1783,10 +1901,13 @@ def test_service_dashboard_shows_free_allowance(
|
||||
],
|
||||
)
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
|
||||
@@ -1802,12 +1923,15 @@ def test_service_dashboard_shows_batched_jobs(
|
||||
):
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats", return_value=mock_daily_stats
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value=mock_daily_stats_by_user,
|
||||
)
|
||||
|
||||
page = client_request.get("main.service_dashboard", service_id=SERVICE_ONE_ID)
|
||||
|
||||
job_table_body = page.find("table", class_="job-table")
|
||||
|
||||
rows = job_table_body.find_all("tbody")[0].find_all("tr")
|
||||
@@ -1815,158 +1939,3 @@ def test_service_dashboard_shows_batched_jobs(
|
||||
assert job_table_body is not None
|
||||
|
||||
assert len(rows) == 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app_with_socketio():
|
||||
app = Flask("app")
|
||||
create_app(app)
|
||||
socketio = SocketIO(app)
|
||||
socketio.on_event("fetch_daily_stats", handle_fetch_daily_stats)
|
||||
socketio.on_event("fetch_daily_stats_by_user", handle_fetch_daily_stats_by_user)
|
||||
return app, socketio
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service_id", "date_range", "expected_call_args"),
|
||||
[
|
||||
(
|
||||
SERVICE_ONE_ID,
|
||||
{"start_date": "2024-01-01", "days": 7},
|
||||
{"service_id": SERVICE_ONE_ID, "start_date": "2024-01-01", "days": 7},
|
||||
),
|
||||
(
|
||||
SERVICE_TWO_ID,
|
||||
{"start_date": "2023-06-01", "days": 7},
|
||||
{"service_id": SERVICE_TWO_ID, "start_date": "2023-06-01", "days": 7},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_fetch_daily_stats(
|
||||
app_with_socketio,
|
||||
mocker,
|
||||
service_id,
|
||||
date_range,
|
||||
expected_call_args,
|
||||
):
|
||||
app, socketio = app_with_socketio
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_stats_date_range", return_value=date_range
|
||||
)
|
||||
|
||||
mock_service_api = mocker.patch(
|
||||
"app.service_api_client.get_service_notification_statistics_by_day",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
with app.test_client() as client:
|
||||
with client.session_transaction() as sess:
|
||||
sess["service_id"] = service_id
|
||||
|
||||
socketio_client = SocketIOTestClient(app, socketio, flask_test_client=client)
|
||||
|
||||
connected = socketio_client.is_connected()
|
||||
assert connected, "Client should be connected"
|
||||
|
||||
socketio_client.emit("fetch_daily_stats")
|
||||
received = socketio_client.get_received()
|
||||
|
||||
mock_service_api.assert_called_once_with(
|
||||
expected_call_args["service_id"],
|
||||
start_date=expected_call_args["start_date"],
|
||||
days=expected_call_args["days"],
|
||||
)
|
||||
assert received, "Should receive a response message"
|
||||
assert received[0]["name"] == "daily_stats_update"
|
||||
assert received[0]["args"][0] == {
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
}
|
||||
|
||||
socketio_client.disconnect()
|
||||
disconnected = not socketio_client.is_connected()
|
||||
assert disconnected, "Client should be disconnected"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service_id", "user_id", "date_range", "expected_call_args", "user"),
|
||||
[
|
||||
(
|
||||
SERVICE_ONE_ID,
|
||||
USER_ONE_ID,
|
||||
{"start_date": "2024-01-01", "days": 7},
|
||||
{
|
||||
"service_id": SERVICE_ONE_ID,
|
||||
"user_id": USER_ONE_ID,
|
||||
"start_date": "2024-01-01",
|
||||
"days": 7,
|
||||
},
|
||||
{"id": USER_ONE_ID, "name": "Test User"},
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_fetch_daily_stats_by_user(
|
||||
app_with_socketio,
|
||||
mocker,
|
||||
service_id,
|
||||
user_id,
|
||||
date_range,
|
||||
expected_call_args,
|
||||
user,
|
||||
):
|
||||
app, socketio = app_with_socketio
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_stats_date_range", return_value=date_range
|
||||
)
|
||||
|
||||
mock_service_api = mocker.patch(
|
||||
"app.service_api_client.get_user_service_notification_statistics_by_day",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
mocker.patch("app.user_api_client.get_user", return_value=user)
|
||||
|
||||
with app.test_client() as client:
|
||||
with client.session_transaction() as sess:
|
||||
sess["service_id"] = service_id
|
||||
sess["user_id"] = user_id
|
||||
|
||||
socketio_client = SocketIOTestClient(app, socketio, flask_test_client=client)
|
||||
|
||||
connected = socketio_client.is_connected()
|
||||
assert connected, "Client should be connected"
|
||||
|
||||
socketio_client.emit("fetch_daily_stats_by_user")
|
||||
received = socketio_client.get_received()
|
||||
|
||||
mock_service_api.assert_called_once_with(
|
||||
expected_call_args["service_id"],
|
||||
expected_call_args["user_id"],
|
||||
start_date=expected_call_args["start_date"],
|
||||
days=expected_call_args["days"],
|
||||
)
|
||||
assert received, "Should receive a response message"
|
||||
assert received[0]["name"] == "daily_stats_by_user_update"
|
||||
assert received[0]["args"][0] == {
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
}
|
||||
|
||||
socketio_client.disconnect()
|
||||
disconnected = not socketio_client.is_connected()
|
||||
assert disconnected, "Client should be disconnected"
|
||||
|
||||
@@ -128,9 +128,26 @@ def test_sign_out_user(
|
||||
# Check we are logged in
|
||||
mocker.patch("app.job_api_client.get_jobs", return_value=MOCK_JOBS)
|
||||
|
||||
date_range = {"start_date": "2024-01-01", "days": 7}
|
||||
|
||||
mocker.patch(
|
||||
"app.notification_api_client.get_notifications_for_service",
|
||||
return_value=FAKE_ONE_OFF_NOTIFICATION,
|
||||
"app.main.views.dashboard.get_daily_stats",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 0, "failure": 0, "requested": 0},
|
||||
"sms": {"delivered": 0, "failure": 1, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
mocker.patch(
|
||||
"app.main.views.dashboard.get_daily_stats_by_user",
|
||||
return_value={
|
||||
date_range["start_date"]: {
|
||||
"email": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
"sms": {"delivered": 1, "failure": 0, "requested": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
client_request.get(
|
||||
|
||||
@@ -21,7 +21,7 @@ Object.defineProperty(HTMLElement.prototype, 'clientWidth', {
|
||||
beforeAll(done => {
|
||||
// Set up the DOM with the D3 script included
|
||||
document.body.innerHTML = `
|
||||
<div id="activityChartContainer">
|
||||
<div id="activityChartContainer" data-daily-stats="{{ daily_stats }}" data-daily_stats_by_user="{{ daily_stats_by_user }}">
|
||||
<form class="usa-form">
|
||||
<label class="usa-label" for="options">Account</label>
|
||||
<select class="usa-select margin-bottom-2" name="options" id="options">
|
||||
@@ -30,7 +30,7 @@ beforeAll(done => {
|
||||
<option value="individual">User Name</option>
|
||||
</select>
|
||||
</form>
|
||||
<div id="activityChart">
|
||||
<div id="activityChart" >
|
||||
<div class="chart-header">
|
||||
<div class="chart-subtitle">Service Name - Last 7 Days</div>
|
||||
<div class="chart-legend" aria-label="Legend"></div>
|
||||
@@ -124,43 +124,3 @@ test('Check HTML content after chart creation', () => {
|
||||
expect(container.querySelector('svg')).not.toBeNull();
|
||||
expect(container.querySelectorAll('rect').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
|
||||
test('Initial fetch data populates chart and table', done => {
|
||||
const mockData = {
|
||||
'2024-07-01': { sms: { delivered: 50, failed: 5 } },
|
||||
'2024-07-02': { sms: { delivered: 60, failed: 2 } },
|
||||
'2024-07-03': { sms: { delivered: 70, failed: 1 } },
|
||||
'2024-07-04': { sms: { delivered: 80, failed: 0 } },
|
||||
'2024-07-05': { sms: { delivered: 90, failed: 3 } },
|
||||
'2024-07-06': { sms: { delivered: 100, failed: 4 } },
|
||||
'2024-07-07': { sms: { delivered: 110, failed: 2 } },
|
||||
};
|
||||
|
||||
const socket = {
|
||||
on: jest.fn((event, callback) => {
|
||||
if (event === 'daily_stats_update') {
|
||||
callback(mockData);
|
||||
done();
|
||||
}
|
||||
}),
|
||||
emit: jest.fn(),
|
||||
};
|
||||
window.io = jest.fn(() => socket);
|
||||
|
||||
document.dispatchEvent(new Event('DOMContentLoaded'));
|
||||
|
||||
setTimeout(() => {
|
||||
const table = document.getElementById('weeklyTable');
|
||||
expect(table).toBeDefined();
|
||||
|
||||
const rows = table.getElementsByTagName('tr');
|
||||
expect(rows.length).toBe(8);
|
||||
|
||||
const firstRowCells = rows[1].getElementsByTagName('td');
|
||||
console.log('First row cells:', firstRowCells);
|
||||
expect(firstRowCells[0].textContent).toBe('07/01/24');
|
||||
expect(firstRowCells[1].textContent).toBe('50');
|
||||
expect(firstRowCells[2].textContent).toBe('5');
|
||||
}, 100);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user