diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 6d34f80b5..fd9e31b91 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -36,7 +36,7 @@ jobs:
- name: Unit tests
working-directory: ./src
- run: docker compose run app python manage.py test
+ run: docker compose run app python manage.py test --parallel
django-migrations-complete:
runs-on: ubuntu-latest
diff --git a/docs/developer/README.md b/docs/developer/README.md
index 57985d6e2..dc4c9ddd2 100644
--- a/docs/developer/README.md
+++ b/docs/developer/README.md
@@ -145,7 +145,7 @@ You can change the logging verbosity, if needed. Do a web search for "django log
## Mock data
-There is a `post_migrate` signal in [signals.py](../../src/registrar/signals.py) that will load the fixtures from [fixtures_user.py](../../src/registrar/fixtures_users.py) and [fixtures_applications.py](../../src/registrar/fixtures_applications.py), giving you some test data to play with while developing.
+[load.py](../../src/registrar/management/commands/load.py) called from docker-compose (locally) and reset-db.yml (upper) loads the fixtures from [fixtures_user.py](../../src/registrar/fixtures_users.py) and [fixtures_applications.py](../../src/registrar/fixtures_applications.py), giving you some test data to play with while developing.
See the [database-access README](./database-access.md) for information on how to pull data to update these fixtures.
diff --git a/src/djangooidc/tests/test_views.py b/src/djangooidc/tests/test_views.py
index 63b23df96..4193f723b 100644
--- a/src/djangooidc/tests/test_views.py
+++ b/src/djangooidc/tests/test_views.py
@@ -35,155 +35,155 @@ class ViewsTest(TestCase):
pass
def test_openid_sets_next(self, mock_client):
- # setup
- callback_url = reverse("openid_login_callback")
- # mock
- mock_client.create_authn_request.side_effect = self.say_hi
- mock_client.get_default_acr_value.side_effect = self.create_acr
- # test
- response = self.client.get(reverse("login"), {"next": callback_url})
- # assert
- session = mock_client.create_authn_request.call_args[0][0]
- self.assertEqual(session["next"], callback_url)
- self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Hi")
+ with less_console_noise():
+ # setup
+ callback_url = reverse("openid_login_callback")
+ # mock
+ mock_client.create_authn_request.side_effect = self.say_hi
+ mock_client.get_default_acr_value.side_effect = self.create_acr
+ # test
+ response = self.client.get(reverse("login"), {"next": callback_url})
+ # assert
+ session = mock_client.create_authn_request.call_args[0][0]
+ self.assertEqual(session["next"], callback_url)
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, "Hi")
def test_openid_raises(self, mock_client):
- # mock
- mock_client.create_authn_request.side_effect = Exception("Test")
- # test
with less_console_noise():
+ # mock
+ mock_client.create_authn_request.side_effect = Exception("Test")
+ # test
response = self.client.get(reverse("login"))
- # assert
- self.assertEqual(response.status_code, 500)
- self.assertTemplateUsed(response, "500.html")
- self.assertIn("Server error", response.content.decode("utf-8"))
+ # assert
+ self.assertEqual(response.status_code, 500)
+ self.assertTemplateUsed(response, "500.html")
+ self.assertIn("Server error", response.content.decode("utf-8"))
def test_callback_with_no_session_state(self, mock_client):
"""If the local session is None (ie the server restarted while user was logged out),
we do not throw an exception. Rather, we attempt to login again."""
- # mock
- mock_client.get_default_acr_value.side_effect = self.create_acr
- mock_client.callback.side_effect = NoStateDefined()
- # test
with less_console_noise():
+ # mock
+ mock_client.get_default_acr_value.side_effect = self.create_acr
+ mock_client.callback.side_effect = NoStateDefined()
+ # test
response = self.client.get(reverse("openid_login_callback"))
- # assert
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, "/")
+ # assert
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response.url, "/")
def test_login_callback_reads_next(self, mock_client):
- # setup
- session = self.client.session
- session["next"] = reverse("logout")
- session.save()
- # mock
- mock_client.callback.side_effect = self.user_info
- # test
- with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
- response = self.client.get(reverse("openid_login_callback"))
- # assert
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, reverse("logout"))
+ with less_console_noise():
+ # setup
+ session = self.client.session
+ session["next"] = reverse("logout")
+ session.save()
+ # mock
+ mock_client.callback.side_effect = self.user_info
+ # test
+ with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
+ response = self.client.get(reverse("openid_login_callback"))
+ # assert
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response.url, reverse("logout"))
def test_login_callback_no_step_up_auth(self, mock_client):
"""Walk through login_callback when requires_step_up_auth returns False
and assert that we have a redirect to /"""
- # setup
- session = self.client.session
- session.save()
- # mock
- mock_client.callback.side_effect = self.user_info
- # test
- with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
- response = self.client.get(reverse("openid_login_callback"))
- # assert
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, "/")
+ with less_console_noise():
+ # setup
+ session = self.client.session
+ session.save()
+ # mock
+ mock_client.callback.side_effect = self.user_info
+ # test
+ with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
+ response = self.client.get(reverse("openid_login_callback"))
+ # assert
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response.url, "/")
def test_requires_step_up_auth(self, mock_client):
"""Invoke login_callback passing it a request when requires_step_up_auth returns True
and assert that session is updated and create_authn_request (mock) is called."""
- # Configure the mock to return an expected value for get_step_up_acr_value
- mock_client.return_value.get_step_up_acr_value.return_value = "step_up_acr_value"
-
- # Create a mock request
- request = self.factory.get("/some-url")
- request.session = {"acr_value": ""}
-
- # Ensure that the CLIENT instance used in login_callback is the mock
- # patch requires_step_up_auth to return True
- with patch("djangooidc.views.requires_step_up_auth", return_value=True), patch(
- "djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
- ) as mock_create_authn_request:
- login_callback(request)
-
- # create_authn_request only gets called when requires_step_up_auth is True
- # and it changes this acr_value in request.session
-
- # Assert that acr_value is no longer empty string
- self.assertNotEqual(request.session["acr_value"], "")
- # And create_authn_request was called again
- mock_create_authn_request.assert_called_once()
+ with less_console_noise():
+ # Configure the mock to return an expected value for get_step_up_acr_value
+ mock_client.return_value.get_step_up_acr_value.return_value = "step_up_acr_value"
+ # Create a mock request
+ request = self.factory.get("/some-url")
+ request.session = {"acr_value": ""}
+ # Ensure that the CLIENT instance used in login_callback is the mock
+ # patch requires_step_up_auth to return True
+ with patch("djangooidc.views.requires_step_up_auth", return_value=True), patch(
+ "djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
+ ) as mock_create_authn_request:
+ login_callback(request)
+ # create_authn_request only gets called when requires_step_up_auth is True
+ # and it changes this acr_value in request.session
+ # Assert that acr_value is no longer empty string
+ self.assertNotEqual(request.session["acr_value"], "")
+ # And create_authn_request was called again
+ mock_create_authn_request.assert_called_once()
def test_does_not_requires_step_up_auth(self, mock_client):
"""Invoke login_callback passing it a request when requires_step_up_auth returns False
and assert that session is not updated and create_authn_request (mock) is not called.
Possibly redundant with test_login_callback_requires_step_up_auth"""
- # Create a mock request
- request = self.factory.get("/some-url")
- request.session = {"acr_value": ""}
-
- # Ensure that the CLIENT instance used in login_callback is the mock
- # patch requires_step_up_auth to return False
- with patch("djangooidc.views.requires_step_up_auth", return_value=False), patch(
- "djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
- ) as mock_create_authn_request:
- login_callback(request)
-
- # create_authn_request only gets called when requires_step_up_auth is True
- # and it changes this acr_value in request.session
-
- # Assert that acr_value is NOT updated by testing that it is still an empty string
- self.assertEqual(request.session["acr_value"], "")
- # Assert create_authn_request was not called
- mock_create_authn_request.assert_not_called()
+ with less_console_noise():
+ # Create a mock request
+ request = self.factory.get("/some-url")
+ request.session = {"acr_value": ""}
+ # Ensure that the CLIENT instance used in login_callback is the mock
+ # patch requires_step_up_auth to return False
+ with patch("djangooidc.views.requires_step_up_auth", return_value=False), patch(
+ "djangooidc.views.CLIENT.create_authn_request", return_value=MagicMock()
+ ) as mock_create_authn_request:
+ login_callback(request)
+ # create_authn_request only gets called when requires_step_up_auth is True
+ # and it changes this acr_value in request.session
+ # Assert that acr_value is NOT updated by testing that it is still an empty string
+ self.assertEqual(request.session["acr_value"], "")
+ # Assert create_authn_request was not called
+ mock_create_authn_request.assert_not_called()
@patch("djangooidc.views.authenticate")
def test_login_callback_raises(self, mock_auth, mock_client):
- # mock
- mock_client.callback.side_effect = self.user_info
- mock_auth.return_value = None
- # test
- with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
- response = self.client.get(reverse("openid_login_callback"))
- # assert
- self.assertEqual(response.status_code, 401)
- self.assertTemplateUsed(response, "401.html")
- self.assertIn("Unauthorized", response.content.decode("utf-8"))
+ with less_console_noise():
+ # mock
+ mock_client.callback.side_effect = self.user_info
+ mock_auth.return_value = None
+ # test
+ with patch("djangooidc.views.requires_step_up_auth", return_value=False), less_console_noise():
+ response = self.client.get(reverse("openid_login_callback"))
+ # assert
+ self.assertEqual(response.status_code, 401)
+ self.assertTemplateUsed(response, "401.html")
+ self.assertIn("Unauthorized", response.content.decode("utf-8"))
def test_logout_redirect_url(self, mock_client):
- # setup
- session = self.client.session
- session["state"] = "TEST" # nosec B105
- session.save()
- # mock
- mock_client.callback.side_effect = self.user_info
- mock_client.registration_response = {"post_logout_redirect_uris": ["http://example.com/back"]}
- mock_client.provider_info = {"end_session_endpoint": "http://example.com/log_me_out"}
- mock_client.client_id = "TEST"
- # test
with less_console_noise():
- response = self.client.get(reverse("logout"))
- # assert
- expected = (
- "http://example.com/log_me_out?client_id=TEST&state"
- "=TEST&post_logout_redirect_uri=http%3A%2F%2Fexample.com%2Fback"
- )
- actual = response.url
- self.assertEqual(response.status_code, 302)
- self.assertEqual(actual, expected)
+ # setup
+ session = self.client.session
+ session["state"] = "TEST" # nosec B105
+ session.save()
+ # mock
+ mock_client.callback.side_effect = self.user_info
+ mock_client.registration_response = {"post_logout_redirect_uris": ["http://example.com/back"]}
+ mock_client.provider_info = {"end_session_endpoint": "http://example.com/log_me_out"}
+ mock_client.client_id = "TEST"
+ # test
+ with less_console_noise():
+ response = self.client.get(reverse("logout"))
+ # assert
+ expected = (
+ "http://example.com/log_me_out?client_id=TEST&state"
+ "=TEST&post_logout_redirect_uri=http%3A%2F%2Fexample.com%2Fback"
+ )
+ actual = response.url
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(actual, expected)
@patch("djangooidc.views.auth_logout")
def test_logout_always_logs_out(self, mock_logout, _):
@@ -194,12 +194,13 @@ class ViewsTest(TestCase):
self.assertTrue(mock_logout.called)
def test_logout_callback_redirects(self, _):
- # setup
- session = self.client.session
- session["next"] = reverse("logout")
- session.save()
- # test
- response = self.client.get(reverse("openid_logout_callback"))
- # assert
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, reverse("logout"))
+ with less_console_noise():
+ # setup
+ session = self.client.session
+ session["next"] = reverse("logout")
+ session.save()
+ # test
+ response = self.client.get(reverse("openid_logout_callback"))
+ # assert
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(response.url, reverse("logout"))
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index ba6530674..fdf069f56 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -25,6 +25,8 @@ services:
- DJANGO_SECRET_KEY=really-long-random-string-BNPecI7+s8jMahQcGHZ3XQ5yUfRrSibdapVLIz0UemdktVPofDKcoy
# Run Django in debug mode on local
- DJANGO_DEBUG=True
+ # Set DJANGO_LOG_LEVEL in env
+ - DJANGO_LOG_LEVEL
# Run Django without production flags
- IS_PRODUCTION=False
# Tell Django where it is being hosted
diff --git a/src/epplibwrapper/tests/common.py b/src/epplibwrapper/tests/common.py
new file mode 100644
index 000000000..122965ae8
--- /dev/null
+++ b/src/epplibwrapper/tests/common.py
@@ -0,0 +1,51 @@
+import os
+import logging
+
+from contextlib import contextmanager
+
+
+def get_handlers():
+ """Obtain pointers to all StreamHandlers."""
+ handlers = {}
+
+ rootlogger = logging.getLogger()
+ for h in rootlogger.handlers:
+ if isinstance(h, logging.StreamHandler):
+ handlers[h.name] = h
+
+ for logger in logging.Logger.manager.loggerDict.values():
+ if not isinstance(logger, logging.PlaceHolder):
+ for h in logger.handlers:
+ if isinstance(h, logging.StreamHandler):
+ handlers[h.name] = h
+
+ return handlers
+
+
+@contextmanager
+def less_console_noise():
+ """
+ Context manager to use in tests to silence console logging.
+
+ This is helpful on tests which trigger console messages
+ (such as errors) which are normal and expected.
+
+ It can easily be removed to debug a failing test.
+ """
+ restore = {}
+ handlers = get_handlers()
+ devnull = open(os.devnull, "w")
+
+ # redirect all the streams
+ for handler in handlers.values():
+ prior = handler.setStream(devnull)
+ restore[handler.name] = prior
+ try:
+ # run the test
+ yield
+ finally:
+ # restore the streams
+ for handler in handlers.values():
+ handler.setStream(restore[handler.name])
+ # close the file we opened
+ devnull.close()
diff --git a/src/epplibwrapper/tests/test_pool.py b/src/epplibwrapper/tests/test_pool.py
index 1c36d26da..f8e556445 100644
--- a/src/epplibwrapper/tests/test_pool.py
+++ b/src/epplibwrapper/tests/test_pool.py
@@ -9,7 +9,7 @@ from epplibwrapper.socket import Socket
from epplibwrapper.utility.pool import EPPConnectionPool
from registrar.models.domain import registry
from contextlib import ExitStack
-
+from .common import less_console_noise
import logging
try:
@@ -135,23 +135,26 @@ class TestConnectionPool(TestCase):
stack.enter_context(patch.object(EPPConnectionPool, "kill_all_connections", do_nothing))
stack.enter_context(patch.object(SocketTransport, "send", self.fake_send))
stack.enter_context(patch.object(SocketTransport, "receive", fake_receive))
- # Restart the connection pool
- registry.start_connection_pool()
- # Pool should be running, and be the right size
- self.assertEqual(registry.pool_status.connection_success, True)
- self.assertEqual(registry.pool_status.pool_running, True)
+ with less_console_noise():
+ # Restart the connection pool
+ registry.start_connection_pool()
+ # Pool should be running, and be the right size
+ self.assertEqual(registry.pool_status.connection_success, True)
+ self.assertEqual(registry.pool_status.pool_running, True)
- # Send a command
- result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
+ # Send a command
+ result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
- # Should this ever fail, it either means that the schema has changed,
- # or the pool is broken.
- # If the schema has changed: Update the associated infoDomain.xml file
- self.assertEqual(result.__dict__, expected_result)
+ # Should this ever fail, it either means that the schema has changed,
+ # or the pool is broken.
+ # If the schema has changed: Update the associated infoDomain.xml file
+ self.assertEqual(result.__dict__, expected_result)
- # The number of open pools should match the number of requested ones.
- # If it is 0, then they failed to open
- self.assertEqual(len(registry._pool.conn), self.pool_options["size"])
+ # The number of open pools should match the number of requested ones.
+ # If it is 0, then they failed to open
+ self.assertEqual(len(registry._pool.conn), self.pool_options["size"])
+ # Kill the connection pool
+ registry.kill_pool()
@patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success)
def test_pool_restarts_on_send(self):
@@ -198,35 +201,43 @@ class TestConnectionPool(TestCase):
xml = (location).read_bytes()
return xml
+ def do_nothing(command):
+ pass
+
# Mock what happens inside the "with"
with ExitStack() as stack:
stack.enter_context(patch.object(EPPConnectionPool, "_create_socket", self.fake_socket))
stack.enter_context(patch.object(Socket, "connect", self.fake_client))
+ stack.enter_context(patch.object(EPPConnectionPool, "kill_all_connections", do_nothing))
stack.enter_context(patch.object(SocketTransport, "send", self.fake_send))
stack.enter_context(patch.object(SocketTransport, "receive", fake_receive))
- # Kill the connection pool
- registry.kill_pool()
+ with less_console_noise():
+ # Start the connection pool
+ registry.start_connection_pool()
+ # Kill the connection pool
+ registry.kill_pool()
- self.assertEqual(registry.pool_status.connection_success, False)
- self.assertEqual(registry.pool_status.pool_running, False)
+ self.assertEqual(registry.pool_status.pool_running, False)
- # An exception should be raised as end user will be informed
- # that they cannot connect to EPP
- with self.assertRaises(RegistryError):
- expected = "InfoDomain failed to execute due to a connection error."
+ # An exception should be raised as end user will be informed
+ # that they cannot connect to EPP
+ with self.assertRaises(RegistryError):
+ expected = "InfoDomain failed to execute due to a connection error."
+ result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
+ self.assertEqual(result, expected)
+
+ # A subsequent command should be successful, as the pool restarts
result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
- self.assertEqual(result, expected)
+ # Should this ever fail, it either means that the schema has changed,
+ # or the pool is broken.
+ # If the schema has changed: Update the associated infoDomain.xml file
+ self.assertEqual(result.__dict__, expected_result)
- # A subsequent command should be successful, as the pool restarts
- result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
- # Should this ever fail, it either means that the schema has changed,
- # or the pool is broken.
- # If the schema has changed: Update the associated infoDomain.xml file
- self.assertEqual(result.__dict__, expected_result)
-
- # The number of open pools should match the number of requested ones.
- # If it is 0, then they failed to open
- self.assertEqual(len(registry._pool.conn), self.pool_options["size"])
+ # The number of open pools should match the number of requested ones.
+ # If it is 0, then they failed to open
+ self.assertEqual(len(registry._pool.conn), self.pool_options["size"])
+ # Kill the connection pool
+ registry.kill_pool()
@patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success)
def test_raises_connection_error(self):
@@ -236,13 +247,16 @@ class TestConnectionPool(TestCase):
with ExitStack() as stack:
stack.enter_context(patch.object(EPPConnectionPool, "_create_socket", self.fake_socket))
stack.enter_context(patch.object(Socket, "connect", self.fake_client))
+ with less_console_noise():
+ # Start the connection pool
+ registry.start_connection_pool()
- # Pool should be running
- self.assertEqual(registry.pool_status.connection_success, True)
- self.assertEqual(registry.pool_status.pool_running, True)
+ # Pool should be running
+ self.assertEqual(registry.pool_status.connection_success, True)
+ self.assertEqual(registry.pool_status.pool_running, True)
- # Try to send a command out - should fail
- with self.assertRaises(RegistryError):
- expected = "InfoDomain failed to execute due to a connection error."
- result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
- self.assertEqual(result, expected)
+ # Try to send a command out - should fail
+ with self.assertRaises(RegistryError):
+ expected = "InfoDomain failed to execute due to a connection error."
+ result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True)
+ self.assertEqual(result, expected)
diff --git a/src/epplibwrapper/utility/pool.py b/src/epplibwrapper/utility/pool.py
index 93edb2782..4f54e14ce 100644
--- a/src/epplibwrapper/utility/pool.py
+++ b/src/epplibwrapper/utility/pool.py
@@ -85,6 +85,21 @@ class EPPConnectionPool(ConnectionPool):
logger.error(message, exc_info=True)
raise PoolError(code=PoolErrorCodes.KEEP_ALIVE_FAILED) from err
+ def _keepalive_periodic(self):
+ """Overriding _keepalive_periodic from geventconnpool so that PoolErrors
+ are properly handled, as opposed to printing to stdout"""
+ delay = float(self.keepalive) / self.size
+ while 1:
+ try:
+ with self.get() as c:
+ self._keepalive(c)
+ except PoolError as err:
+ logger.error(err.message, exc_info=True)
+ except self.exc_classes:
+ # Nothing to do, the pool will generate a new connection later
+ pass
+ gevent.sleep(delay)
+
def _create_socket(self, client, login) -> Socket:
"""Creates and returns a socket instance"""
socket = Socket(client, login)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 325081575..1bfda0b84 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -626,7 +626,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
search_help_text = "Search by domain."
fieldsets = [
- (None, {"fields": ["creator", "domain_application"]}),
+ (None, {"fields": ["creator", "domain_application", "notes"]}),
(
"Type of organization",
{
@@ -677,6 +677,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
"type_of_work",
"more_organization_information",
"domain",
+ "domain_application",
"submitter",
"no_other_contacts_rationale",
"anything_else",
@@ -793,7 +794,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
# Detail view
form = DomainApplicationAdminForm
fieldsets = [
- (None, {"fields": ["status", "investigator", "creator", "approved_domain"]}),
+ (None, {"fields": ["status", "investigator", "creator", "approved_domain", "notes"]}),
(
"Type of organization",
{
@@ -845,6 +846,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
"creator",
"about_your_organization",
"requested_domain",
+ "approved_domain",
"alternative_domains",
"purpose",
"submitter",
@@ -1047,6 +1049,13 @@ class DomainAdmin(ListHeaderAdmin):
"deleted",
]
+ fieldsets = (
+ (
+ None,
+ {"fields": ["name", "state", "expiration_date", "first_ready", "deleted"]},
+ ),
+ )
+
# this ordering effects the ordering of results
# in autocomplete_fields for domain
ordering = ["name"]
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index cdbbc83ee..866c7bd7d 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -283,19 +283,20 @@ function enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, elementPk,
(function (){
// Get the current date in the format YYYY-MM-DD
- var currentDate = new Date().toISOString().split('T')[0];
+ let currentDate = new Date().toISOString().split('T')[0];
// Default the value of the start date input field to the current date
let startDateInput =document.getElementById('start');
- startDateInput.value = currentDate;
-
+
// Default the value of the end date input field to the current date
let endDateInput =document.getElementById('end');
- endDateInput.value = currentDate;
let exportGrowthReportButton = document.getElementById('exportLink');
if (exportGrowthReportButton) {
+ startDateInput.value = currentDate;
+ endDateInput.value = currentDate;
+
exportGrowthReportButton.addEventListener('click', function() {
// Get the selected start and end dates
let startDate = startDateInput.value;
diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss
index 02089ec6d..2f4121399 100644
--- a/src/registrar/assets/sass/_theme/_buttons.scss
+++ b/src/registrar/assets/sass/_theme/_buttons.scss
@@ -44,6 +44,22 @@ a.usa-button.disabled-link:focus {
color: #454545 !important
}
+a.usa-button--unstyled.disabled-link,
+a.usa-button--unstyled.disabled-link:hover,
+a.usa-button--unstyled.disabled-link:focus {
+ cursor: not-allowed !important;
+ outline: none !important;
+ text-decoration: none !important;
+}
+
+.usa-button--unstyled.disabled-button,
+.usa-button--unstyled.disabled-link:hover,
+.usa-button--unstyled.disabled-link:focus {
+ cursor: not-allowed !important;
+ outline: none !important;
+ text-decoration: none !important;
+}
+
a.usa-button:not(.usa-button--unstyled, .usa-button--outline) {
color: color('white');
}
diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py
index ba5ae22cc..f6378b555 100644
--- a/src/registrar/config/urls.py
+++ b/src/registrar/config/urls.py
@@ -142,6 +142,11 @@ urlpatterns = [
views.DomainApplicationDeleteView.as_view(http_method_names=["post"]),
name="application-delete",
),
+ path(
+ "domain/
Names that uniquely apply to your organization are likely to be approved over names that could also apply to other organizations. In most instances, this requires including your state’s two-letter abbreviation.
+Names that uniquely apply to your organization are likely to be approved over names that could also apply to other organizations. + {% if not is_federal %}In most instances, this requires including your state’s two-letter abbreviation.{% endif %}
Requests for your organization’s initials or an abbreviated name might not be approved, but we encourage you to request the name you want.
diff --git a/src/registrar/templates/application_form.html b/src/registrar/templates/application_form.html index 524045fbe..965967072 100644 --- a/src/registrar/templates/application_form.html +++ b/src/registrar/templates/application_form.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% load static form_helpers url_helpers %} -{% block title %}Request a .gov domain | {{form_titles|get_item:steps.current}} | {% endblock %} +{% block title %}{{form_titles|get_item:steps.current}} | Request a .gov | {% endblock %} {% block content %}