diff --git a/specifyweb/backend/businessrules/tests/test_geologictimeperiod.py b/specifyweb/backend/businessrules/tests/test_geologictimeperiod.py
index 75430a3652a..c241b0ce573 100644
--- a/specifyweb/backend/businessrules/tests/test_geologictimeperiod.py
+++ b/specifyweb/backend/businessrules/tests/test_geologictimeperiod.py
@@ -1,8 +1,13 @@
from unittest import skip
+from decimal import Decimal
+
from django.db.models import ProtectedError
from specifyweb.specify import models
+from specifyweb.specify.api.crud import delete_resource, post_resource, update_obj
+from specifyweb.specify.api.serializers import uri_for_model
from specifyweb.specify.tests.test_api import ApiTests
+from ..exceptions import TreeBusinessRuleException
class Geologictimeperiod(ApiTests):
def setUp(self):
@@ -14,6 +19,224 @@ def setUp(self):
definitionitem=self.geologictimeperiodtreedef.treedefitems.create(
name="root",
rankid=0))
+ self.era_rank = self.rootgtp.definitionitem.children.create(
+ name="Era",
+ rankid=100,
+ treedef=self.geologictimeperiodtreedef,
+ )
+
+ def _period_data(self, name, **overrides):
+ data = {
+ "name": name,
+ "parent": uri_for_model("geologictimeperiod", self.rootgtp.id),
+ "definitionitem": uri_for_model(
+ "geologictimeperiodtreedefitem", self.era_rank.id
+ ),
+ "remarks": "Test period",
+ "startperiod": 251.9,
+ "endperiod": 66.0,
+ "isbiostrat": True,
+ }
+ data.update(overrides)
+ return data
+
+ def _create_period(self, name, **overrides):
+ return post_resource(
+ self.collection,
+ self.agent,
+ "geologictimeperiod",
+ self._period_data(name, **overrides),
+ )
+
+ def _create_existing_period(self, name):
+ return self.rootgtp.children.create(
+ name=name,
+ definition=self.geologictimeperiodtreedef,
+ definitionitem=self.era_rank,
+ rankid=self.era_rank.rankid,
+ remarks="Created before this version",
+ )
+
+ def test_add_geologictimeperiod(self):
+ period = self._create_period("Mesozoic")
+
+ self.assertIsNotNone(period.id)
+ self.assertExists(
+ models.Geologictimeperiod.objects.filter(
+ id=period.id,
+ name="Mesozoic",
+ parent=self.rootgtp,
+ )
+ )
+ self.assertEqual(period.definition_id, self.geologictimeperiodtreedef.id)
+ self.assertEqual(period.definitionitem_id, self.era_rank.id)
+ self.assertEqual(period.rankid, self.era_rank.rankid)
+
+ period.refresh_from_db()
+ self.assertEqual(period.remarks, "Test period")
+ self.assertEqual(period.startperiod, Decimal("251.9"))
+ self.assertEqual(period.endperiod, Decimal("66.0"))
+ self.assertIs(period.isbiostrat, True)
+
+ period.delete()
+
+ def test_geologictimeperiod_rank_must_be_below_parent(self):
+ period_rank = self.era_rank.children.create(
+ name="Period",
+ rankid=200,
+ treedef=self.geologictimeperiodtreedef,
+ )
+ parent = self.rootgtp.children.create(
+ name="Cenozoic",
+ definition=self.geologictimeperiodtreedef,
+ definitionitem=period_rank,
+ rankid=period_rank.rankid,
+ )
+
+ with self.assertRaises(TreeBusinessRuleException):
+ models.Geologictimeperiod.objects.create(
+ name="Invalid child",
+ parent=parent,
+ definition=self.geologictimeperiodtreedef,
+ definitionitem=self.era_rank,
+ rankid=self.era_rank.rankid,
+ )
+
+ def test_geologictimeperiod_optional_fields_round_trip(self):
+ period = self._create_period(
+ "Cenozoic",
+ remarks="Optional fields",
+ startperiod=66.0,
+ endperiod=23.03,
+ startuncertainty=0.5,
+ enduncertainty=0.2,
+ standard="ICS",
+ text1="Text one",
+ text2="Text two",
+ isbiostrat=False,
+ )
+
+ period.refresh_from_db()
+ self.assertEqual(period.remarks, "Optional fields")
+ self.assertEqual(period.startperiod, Decimal("66.0"))
+ self.assertEqual(period.endperiod, Decimal("23.03"))
+ self.assertEqual(period.startuncertainty, Decimal("0.5"))
+ self.assertEqual(period.enduncertainty, Decimal("0.2"))
+ self.assertEqual(period.standard, "ICS")
+ self.assertEqual(period.text1, "Text one")
+ self.assertEqual(period.text2, "Text two")
+ self.assertFalse(period.isbiostrat)
+
+ period.delete()
+
+ def test_clone_geologictimeperiod(self):
+ original = self._create_period(
+ "Paleozoic",
+ remarks="Original remarks",
+ startperiod=541.0,
+ endperiod=252.0,
+ isbiostrat=False,
+ )
+
+ clone = self._create_period(
+ original.name,
+ remarks=original.remarks,
+ startperiod=original.startperiod,
+ endperiod=original.endperiod,
+ isbiostrat=original.isbiostrat,
+ )
+
+ self.assertNotEqual(clone.id, original.id)
+ self.assertEqual(clone.name, original.name)
+ self.assertEqual(clone.parent_id, original.parent_id)
+ self.assertEqual(clone.definitionitem_id, original.definitionitem_id)
+ self.assertEqual(clone.remarks, original.remarks)
+ self.assertEqual(clone.startperiod, original.startperiod)
+ self.assertEqual(clone.endperiod, original.endperiod)
+ self.assertEqual(clone.isbiostrat, original.isbiostrat)
+
+ clone.refresh_from_db()
+ self.assertEqual(clone.remarks, "Original remarks")
+ self.assertEqual(clone.startperiod, Decimal("541.0"))
+ self.assertEqual(clone.endperiod, Decimal("252.0"))
+ self.assertIs(clone.isbiostrat, False)
+
+ original.refresh_from_db()
+ self.assertEqual(original.name, "Paleozoic")
+
+ clone.delete()
+ original.delete()
+
+ def test_delete_new_geologictimeperiod(self):
+ period = self._create_period("Triassic")
+ period_id = period.id
+
+ delete_resource(
+ self.collection,
+ self.agent,
+ "geologictimeperiod",
+ period_id,
+ period.version,
+ )
+
+ self.assertNotExists(
+ models.Geologictimeperiod.objects.filter(id=period_id)
+ )
+
+ def test_edit_geologictimeperiod_created_in_previous_version(self):
+ period = self._create_existing_period("Jurassic")
+
+ update_obj(
+ self.collection,
+ self.agent,
+ "geologictimeperiod",
+ period.id,
+ period.version,
+ {"name": "Jurassic (updated)", "remarks": "Updated remarks"},
+ )
+
+ period.refresh_from_db()
+ self.assertEqual(period.name, "Jurassic (updated)")
+ self.assertEqual(period.remarks, "Updated remarks")
+ self.assertEqual(period.version, 1)
+
+ period.delete()
+
+ def test_edit_geologictimeperiod_with_nonzero_version(self):
+ period = self._create_existing_period("Triassic")
+ models.Geologictimeperiod.objects.filter(id=period.id).update(version=2)
+ period.refresh_from_db()
+
+ update_obj(
+ self.collection,
+ self.agent,
+ "geologictimeperiod",
+ period.id,
+ period.version,
+ {"name": "Triassic (updated)"},
+ )
+
+ period.refresh_from_db()
+ self.assertEqual(period.name, "Triassic (updated)")
+ self.assertEqual(period.version, 3)
+
+ period.delete()
+
+ def test_delete_geologictimeperiod_created_in_previous_version(self):
+ period = self._create_existing_period("Cretaceous")
+ period_id = period.id
+
+ delete_resource(
+ self.collection,
+ self.agent,
+ "geologictimeperiod",
+ period_id,
+ period.version,
+ )
+
+ self.assertNotExists(
+ models.Geologictimeperiod.objects.filter(id=period_id)
+ )
def test_delete_blocked_by_biostratspaleocontext(self):
self.rootgtp.biostratspaleocontext.create(
diff --git a/specifyweb/frontend/js_src/lib/components/Attachments/__tests__/ChronoChart.test.tsx b/specifyweb/frontend/js_src/lib/components/Attachments/__tests__/ChronoChart.test.tsx
index 268d670152b..62dc8582c72 100644
--- a/specifyweb/frontend/js_src/lib/components/Attachments/__tests__/ChronoChart.test.tsx
+++ b/specifyweb/frontend/js_src/lib/components/Attachments/__tests__/ChronoChart.test.tsx
@@ -10,6 +10,21 @@ describe('ChronoChart', () => {
expect(asFragment()).toMatchSnapshot();
});
+ test('clock icon opens the chronostratigraphic chart', async () => {
+ const { getByRole, user } = mount(
+
+
+
+ );
+
+ await user.click(
+ getByRole('button', { name: 'Chronostratigraphic Chart' })
+ );
+
+ expect(getByRole('dialog')).toBeInTheDocument();
+ expect(getByRole('img', { name: 'Chrono Chart' })).toBeInTheDocument();
+ });
+
test('dialog open and close', async () => {
const { getAllByRole, getByRole, queryAllByRole, user } = mount(