Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session timeout javascript #295

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

jinnashravan
Copy link
Contributor

This is Javascript version, where we show a session timeout warning banner.
After 15 minutes, we show a warning. If the user clicks continue we extend the session for another 20 minutes.
If no activity happens in 20 minutes, we show a sesssion ended screen.

1. After 20 minutes cookie session timesout and shows a session-ended page.
@jinnashravan jinnashravan requested review from maxf and dhvander January 28, 2025 20:47
Session Timeout Javascript verion

In settings session cookie age is set to 20 minutes.
After 15 minutes, show a warning dialog about session expiry
After 20 minutes, show the session ended page.
@jinnashravan jinnashravan force-pushed the session_timeout_javascript branch from 6cd5a31 to 342f408 Compare January 28, 2025 21:09
Using the clock and tick API's of cypress to override the time function.
Warning appears after 15 minutes and session ends after 20 minutes if no action is taken.
@jinnashravan jinnashravan force-pushed the session_timeout_javascript branch from 2570a52 to f308e4c Compare January 28, 2025 21:34
<div class="govuk-notification-banner__content">
<h2 class="govuk-heading-s">Your session can time out</h2>
<div class="govuk-body">
<p>We will delete your answers if you do not do anything for 20 minutes and you will have to start again.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the 20 be read from the settings value?

</h1>

<p class="govuk-body">
Your session has ended because you haven't done anything for over 20 minutes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

{% load csp %}

{% block title %}Session Ended{% endblock %}
{% block session_ended %}{% endblock %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment here to say why you have overidden the block



def reset_timer(request):
logger.info("Resetting the session timer")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the log statements?


</body>
{% block session_ended %}
<script type="text/javascript" nonce="{{ request.csp_nonce }}">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comments and remove log statements that are not needed

cypress/e2e/session_timeout.cy.js Outdated Show resolved Hide resolved
background: rgb(243, 242, 241);
color: rgb(209, 49, 24);
font-size: 16px;
background: rgb(243, 242, 241);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix all your indentation. It should be 2 white space chars everywhere for CSS, HTML and JS

request_a_govuk_domain/request/forms.py Show resolved Hide resolved
{% endif %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0b0c0c">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Sets the auto referesh to match with session cookie age which is 20 minutes -->
<!-- <meta http-equiv="refresh" content="1200; url=/session-ended/"> -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this removed? How does it work when JS is disabled?

@maxf maxf force-pushed the session_timeout_javascript branch from f308e4c to b28cb2b Compare January 29, 2025 17:04
@jinnashravan jinnashravan force-pushed the session_timeout_javascript branch from b28cb2b to f308e4c Compare January 29, 2025 20:53
Removed log messages and using SESSION_TIMEOUT param from setting in HTML.
Updated Cypress tests to check for model_dialog not be visible at start.
@@ -1,19 +1,22 @@
{% load static i18n govuk_frontend_django csp %}<!DOCTYPE html>
<html lang="en" class="govuk-template">
<head>
<head>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't break the indentation unless there's a good reason. Otherwise it's very difficult to check what's actually changed


</body>
{% block session_ended %} <!-- session-ended block to override in child template -->
<script type="text/javascript" nonce="{{ request.csp_nonce }}">
var warningTimerId = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use const or let. var should be avoided these days


// Show warning dialog 15 minutes before session expiry
function showWarning() {
var warningDiv = document.getElementById('modal_dialog');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use 2 whitespace chars for indentation, as elsewhere


{% block content %}
{% load custom_filters %}
<div class="govuk-grid-row">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 indent



@register.filter
def divide_by_60(value):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type hints would be useful here and wherever you can add them in your new code

function resetTimers() {
clearTimeout(warningTimerId);
clearTimeout(expiryTimerId);
let sessionCookieAge = "{{ SESSION_TIMEOUT|safe }}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should be a const and you should remove the quote marks as it's supposed to be a number, not a string.

clearTimeout(warningTimerId);
clearTimeout(expiryTimerId);
let sessionCookieAge = "{{ SESSION_TIMEOUT|safe }}";
warningTimerId = setTimeout(showWarning, (sessionCookieAge - 300) * 1000); // 15 minutes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning timeout duration should also be a setting like SESSION_TIMEOUT

{% load custom_filters %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
{% error_summary form %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a form. Can the button just link to the start page (if that resets the session)?

Comment on lines 12 to 17
cy.get('.govuk-grid-column-two-thirds > .govuk-button').click();
cy.get('#id_registrar_organisation').clear().type('WeRegister')
cy.get('#id_registrar_name').clear('Test');
cy.get('#id_registrar_phone').clear('01225672345');
cy.get('#id_registrar_email').clear('[email protected]');
cy.get('#id_submit').click();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could reduce this to 1 line with goToRegistrantType

cy.start()
});

it('session_timeout_dialog_success', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This string is supposed to be human-readable when looking at test results to understand what the test does. Here you could use it('Displays the timeout warning after 15 minutes', for instance

Comment on lines 30 to 35
cy.get('.govuk-grid-column-two-thirds > .govuk-button').click();
cy.get('#id_registrar_organisation').clear().type('WeRegister')
cy.get('#id_registrar_name').clear('Test');
cy.get('#id_registrar_phone').clear('01225672345');
cy.get('#id_registrar_email').clear('[email protected]');
cy.get('#id_submit').click();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, use the utility function.

cy.get('#modal_dialog').should('not.be.visible');

// Check if the user is not redirected to the session-ended page
cy.url().should('include', '/session-ended/');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an extra step to check if when the Continue button is clicked you go back to the start page.


});

it('session_timeout_session_ended', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try a test with multiple tabs? Since this is what justified the server-side timeout, we should have an example where the multi-tab handling works.

request_a_govuk_domain/request/forms.py Show resolved Hide resolved
<p>We will delete your answers if you do not do anything for {{ SESSION_TIMEOUT|divide_by_60 }} minutes and you will have to start
again.</p>
</div>
<button class="govuk-button dialog-button js-dialog-close" data-module="govuk-button" id="session-continue">Continue</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this data-module attribute used?

cy.clock();
});

it('Displays the timeout warning after 15 minutes', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the timeout duration is a parameter you should change the description to something like
"displays the timeout warning after the preset duration".

// check if model_dialog doesn't exist
cy.get('#modal_dialog').should('not.be.visible');
// clock tick
cy.tick(15 * 60 * 1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tricky here as you can't reuse SESSION_COOKIE_AGE. However you could redeclare the same variables at the top of this file, with the same value as in the python code, or different ones, it doesn't matter.

cy.goToRegistrantType()

// clock tick
cy.tick(15 * 60 * 1000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same. Use a constant. Also a few times in your code below.

it('Displays the timeout warning after 15 minutes and continue the session', () => {
// go to RegistrantType page
cy.goToRegistrantType()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just check that the modal dialog isn't visible at this point. It makes the test easier to understand: 1. check it's not visible, 2. let time pass, 3. check if it's visible

@@ -148,7 +150,64 @@
<script nonce="{{request.csp_nonce}}" src='{% static "js.cookie.min.js" %}'></script>
<script nonce="{{request.csp_nonce}}" src='{% static "cookies.js" %}'></script>
{% endif %}

{% load custom_filters %}
<div id="modal_dialog" class="modal">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use a more specific id, like "timeout-warning-modal" ? Also as a reminder, the convention for html ids is to use hyphens not underscores if possible


// Show warning dialog 15 minutes before session expiry
function showWarning() {
let warningDiv = document.getElementById('modal_dialog');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 character indent please

<div class="govuk-grid-column-two-thirds">
{% error_summary form %}
<h1 class="govuk-heading-xl">
Registrar details
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 character indent please

@@ -14,6 +14,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="theme-color" content="#0b0c0c">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- Sets the auto referesh to match with session cookie age which is 20 minutes -->
<meta http-equiv="refresh" content="1200; url=/session-ended/">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. use the variable
  2. You should put this meta tag in a noscript tag I think. Otherwise, if the user has JS there's a risk of both timeout mechanisms to interfere.

</h2>
</div>
<div class="govuk-notification-banner__content">
<h2 class="govuk-heading-s">Your session can time out</h2>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure your indentation is correct everywhere once and for all, so I don't have to remind you multiple times.



def reset_timer(request):
return HttpResponse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a docstring, otherwise this function might seem confusing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants