diff --git a/Makefile b/Makefile
index a6b8dc5972cf14e7a4f9dfeeefbc2de73c120336..8a0dc77d8ec252c8ccb6d4c08081ac130dd664b1 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,16 @@ release: require-version
 worker:
 	celery worker -A arkindex.project -l INFO --purge
 
+test-fixtures:
+	export PGPASSWORD=devdata
+	psql -h 127.0.0.1 -p 9100 -U devuser -c 'ALTER DATABASE arkindex_dev RENAME TO arkindex_tmp_fixtures' template1
+	psql -h 127.0.0.1 -p 9100 -U devuser -c 'CREATE DATABASE arkindex_dev' template1
+	arkindex/manage.py migrate
+	arkindex/manage.py build_fixtures
+	arkindex/manage.py dumpdata --indent 4 documents images users > arkindex/documents/fixtures/data.json
+	psql -h 127.0.0.1 -p 9100 -U devuser -c 'DROP DATABASE arkindex_dev' template1
+	psql -h 127.0.0.1 -p 9100 -U devuser -c 'ALTER DATABASE arkindex_tmp_fixtures RENAME TO arkindex_dev' template1
+
 require-docker-auth:
 	@grep registry.gitlab.com ~/.docker/config.json > /dev/null || (echo "Docker Login on registry.gitlab.com"; docker login registry.gitlab.com)
 
diff --git a/arkindex/documents/fixtures/data.json b/arkindex/documents/fixtures/data.json
new file mode 100644
index 0000000000000000000000000000000000000000..3bfd00dbe35da109af7f60928d53d9f086fd8621
--- /dev/null
+++ b/arkindex/documents/fixtures/data.json
@@ -0,0 +1,1135 @@
+[
+{
+    "model": "documents.corpus",
+    "pk": "himanis",
+    "fields": {
+        "name": "Himanis"
+    }
+},
+{
+    "model": "documents.corpus",
+    "pk": "test",
+    "fields": {
+        "name": "Unit Tests"
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "06592ee7-fc2d-4477-aa47-1ce06268dd8e",
+    "fields": {
+        "element": "ba22e17c-b93f-4683-b869-40588b483558",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "09586b91-fed7-4a91-a4be-00a12d6b9b83",
+    "fields": {
+        "element": "cfd4952d-41ff-45f0-abe4-a904acf5350b",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "11eb3635-8c5a-4dc4-97d9-906467d92cb9",
+    "fields": {
+        "element": "c6b3f72f-1373-45c7-a218-c9b0bcf1c70f",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "157cfbab-6a6f-49cf-8dd3-212f5f277e68",
+    "fields": {
+        "element": "bc9f1cbd-0202-4075-9150-7e256bdbd485",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"a83083ae-6db0-4d3f-9dcb-c49e2a232696\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "4391ca9f-fa02-4540-aacd-8eb81dc19b29",
+    "fields": {
+        "element": "a83083ae-6db0-4d3f-9dcb-c49e2a232696",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 4
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "4daddfe1-b994-4609-a7df-3119980c1b66",
+    "fields": {
+        "element": "f52d8907-aeac-45b1-b584-99c76a31f77b",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ca3fa6e6-838b-4683-8cf9-f6ff75fa8e36\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "5039aafb-c74e-4658-9527-08f474fa1dd8",
+    "fields": {
+        "element": "bb32de06-561c-4c0d-94e0-c43720079cc3",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ba22e17c-b93f-4683-b869-40588b483558\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "59e8a09c-865c-4846-af27-665c7501d74c",
+    "fields": {
+        "element": "6bf20744-97fa-4372-bcc0-5458bb5de3ce",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"c21ff1e2-694d-4310-97b6-d4a31cf0bfa3\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "613d75de-cdf0-4303-90d0-98552d2fa2c6",
+    "fields": {
+        "element": "ca3fa6e6-838b-4683-8cf9-f6ff75fa8e36",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "6373dce1-99c2-4405-8335-d5badae67780",
+    "fields": {
+        "element": "c31fa532-b498-4b22-9b81-5110ef02971d",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"c21ff1e2-694d-4310-97b6-d4a31cf0bfa3\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "671a39a9-a6c8-491b-a5fa-6ccec6b78358",
+    "fields": {
+        "element": "59741867-4b09-4874-afab-86f931d2e223",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"c21ff1e2-694d-4310-97b6-d4a31cf0bfa3\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "7426e036-a6f5-462e-bb3b-52a8d6750a56",
+    "fields": {
+        "element": "4d9be55f-e873-4e21-81e8-144414dc4668",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"3901fbac-ab69-4fa5-9e8e-e973ec684873\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "83f8aa10-f2df-446e-8c79-a185f08ad499",
+    "fields": {
+        "element": "c6d1ae4d-5bf1-4167-ae28-6518c1b572c2",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ab6fa45a-f1fc-4b95-a0fb-db59b5edd717\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "978cc83e-2a8e-4416-9db1-8d820007795e",
+    "fields": {
+        "element": "8069f69b-3f46-47bf-9492-4aeaca1d3a50",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ba22e17c-b93f-4683-b869-40588b483558\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "9d6e58d9-b44c-4764-ba98-f7b592c03a35",
+    "fields": {
+        "element": "3901fbac-ab69-4fa5-9e8e-e973ec684873",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 3
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "a5416544-7779-4e08-973e-67f7f241b8a5",
+    "fields": {
+        "element": "280577a2-37ea-4283-bb8a-5a41bd4158be",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"fd3ee4b9-edd2-4f82-9216-a1160bf890d4\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "ae129f4d-44cd-4521-a366-f1e6308c2c0e",
+    "fields": {
+        "element": "6c17e8e6-71af-4a7d-8bdb-f21532fc024d",
+        "path": "[\"1b2ea32f-6009-4a4f-aa78-fafebbbcbacb\", \"0a47d859-a5ac-41c6-97d8-878da5fefbf1\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "b493410b-c0fa-4a80-87b0-5ba50bff6440",
+    "fields": {
+        "element": "c21ff1e2-694d-4310-97b6-d4a31cf0bfa3",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "b92d349a-df0b-4451-8e28-8d6790af5e46",
+    "fields": {
+        "element": "fd3ee4b9-edd2-4f82-9216-a1160bf890d4",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "ba4f90af-9d39-45f9-b2ba-6ac06780563e",
+    "fields": {
+        "element": "9b857bbf-c8b6-4e8c-8783-18bf1f033290",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"fd3ee4b9-edd2-4f82-9216-a1160bf890d4\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "c256c25c-16d0-4ce1-9967-e95799b2c3c8",
+    "fields": {
+        "element": "478a271b-a93b-4730-89fc-8b486fd7b0f2",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"cfd4952d-41ff-45f0-abe4-a904acf5350b\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "c8176f15-12da-4fbf-a643-6b9b97ff3b3d",
+    "fields": {
+        "element": "0a47d859-a5ac-41c6-97d8-878da5fefbf1",
+        "path": "[\"1b2ea32f-6009-4a4f-aa78-fafebbbcbacb\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "ce847918-cffc-4665-bb5c-df93ee05aff5",
+    "fields": {
+        "element": "87f549c2-581d-4290-b641-75e73926968e",
+        "path": "[\"1b2ea32f-6009-4a4f-aa78-fafebbbcbacb\", \"0a47d859-a5ac-41c6-97d8-878da5fefbf1\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "d0d0e0c9-4712-420f-8026-c5994c476dd1",
+    "fields": {
+        "element": "ce80f464-5f47-4155-b596-bafe3465c102",
+        "path": "[\"1b2ea32f-6009-4a4f-aa78-fafebbbcbacb\", \"0a47d859-a5ac-41c6-97d8-878da5fefbf1\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "eb6d5273-ee51-4ffe-8a07-7d4e92b41368",
+    "fields": {
+        "element": "1327dc18-e5c7-44b0-91e6-9dfee3417ce9",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ab6fa45a-f1fc-4b95-a0fb-db59b5edd717\"]",
+        "ordering": 0
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "f00c6557-0585-4535-88fa-34a5262dc9c6",
+    "fields": {
+        "element": "58343ea9-176f-434c-a9ed-22f975b79968",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ba22e17c-b93f-4683-b869-40588b483558\"]",
+        "ordering": 2
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "f230e413-de34-4d2b-9698-5816bc72eea5",
+    "fields": {
+        "element": "ab6fa45a-f1fc-4b95-a0fb-db59b5edd717",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.elementpath",
+    "pk": "f81727f5-1f9f-4d9b-b945-fd4b696bb1e1",
+    "fields": {
+        "element": "9ef69659-7f7c-4204-9ceb-dbdc2d8daec3",
+        "path": "[\"fa275cab-d830-4f08-a227-6dd4b70ee377\", \"c6b3f72f-1373-45c7-a218-c9b0bcf1c70f\", \"ab6fa45a-f1fc-4b95-a0fb-db59b5edd717\"]",
+        "ordering": 1
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "0a47d859-a5ac-41c6-97d8-878da5fefbf1",
+    "fields": {
+        "created": "2018-07-24T08:18:54.839Z",
+        "updated": "2018-07-24T08:18:54.839Z",
+        "corpus": "test",
+        "type": "register",
+        "name": "Register 2",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "1327dc18-e5c7-44b0-91e6-9dfee3417ce9",
+    "fields": {
+        "created": "2018-07-24T08:18:54.980Z",
+        "updated": "2018-07-24T08:18:54.980Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "d94e1f63-584d-423f-9d66-966362e413a5"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "1b2ea32f-6009-4a4f-aa78-fafebbbcbacb",
+    "fields": {
+        "created": "2018-07-24T08:18:54.831Z",
+        "updated": "2018-07-24T08:18:54.831Z",
+        "corpus": "test",
+        "type": "volume",
+        "name": "Volume 2",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "280577a2-37ea-4283-bb8a-5a41bd4158be",
+    "fields": {
+        "created": "2018-07-24T08:18:55.109Z",
+        "updated": "2018-07-24T08:18:55.109Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface C",
+        "zone": "ee7b9b2b-ee1d-49fc-8192-a03c16a03364"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "3901fbac-ab69-4fa5-9e8e-e973ec684873",
+    "fields": {
+        "created": "2018-07-24T08:18:55.059Z",
+        "updated": "2018-07-24T08:18:55.059Z",
+        "corpus": "test",
+        "type": "act",
+        "name": "Act 4",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "478a271b-a93b-4730-89fc-8b486fd7b0f2",
+    "fields": {
+        "created": "2018-07-24T08:18:55.103Z",
+        "updated": "2018-07-24T08:18:55.103Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface A",
+        "zone": "b968bc1a-2c25-47e3-b49b-d6a05dc6d245"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "4d9be55f-e873-4e21-81e8-144414dc4668",
+    "fields": {
+        "created": "2018-07-24T08:18:55.114Z",
+        "updated": "2018-07-24T08:18:55.114Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface E",
+        "zone": "8b777c07-2cf5-4b47-b407-a19dfc27fcc9"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "58343ea9-176f-434c-a9ed-22f975b79968",
+    "fields": {
+        "created": "2018-07-24T08:18:55.041Z",
+        "updated": "2018-07-24T08:18:55.041Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "c58b425e-1bdf-4c0d-860c-28e6793db75f"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "59741867-4b09-4874-afab-86f931d2e223",
+    "fields": {
+        "created": "2018-07-24T08:18:54.945Z",
+        "updated": "2018-07-24T08:18:54.945Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "cc592547-529e-4289-8351-53fe6a5cc55c"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "6bf20744-97fa-4372-bcc0-5458bb5de3ce",
+    "fields": {
+        "created": "2018-07-24T08:18:54.968Z",
+        "updated": "2018-07-24T08:18:54.968Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "af98c80b-e1b5-4b05-9771-d28e9e543c85"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "6c17e8e6-71af-4a7d-8bdb-f21532fc024d",
+    "fields": {
+        "created": "2018-07-24T08:18:54.916Z",
+        "updated": "2018-07-24T08:18:54.916Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 2, page 1v",
+        "zone": "1542f839-401b-4cf7-8ef1-afc02f6f3cc1"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "8069f69b-3f46-47bf-9492-4aeaca1d3a50",
+    "fields": {
+        "created": "2018-07-24T08:18:55.029Z",
+        "updated": "2018-07-24T08:18:55.029Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "c50126f5-682d-42ed-ab2d-f359717b81ad"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "87f549c2-581d-4290-b641-75e73926968e",
+    "fields": {
+        "created": "2018-07-24T08:18:54.913Z",
+        "updated": "2018-07-24T08:18:54.913Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 2, page 1r",
+        "zone": "0554b559-3f85-40d7-b531-9c4a159e06e2"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "9b857bbf-c8b6-4e8c-8783-18bf1f033290",
+    "fields": {
+        "created": "2018-07-24T08:18:55.106Z",
+        "updated": "2018-07-24T08:18:55.106Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface B",
+        "zone": "26eb4783-b8c9-4768-b55f-0f61f3512659"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "9ef69659-7f7c-4204-9ceb-dbdc2d8daec3",
+    "fields": {
+        "created": "2018-07-24T08:18:54.992Z",
+        "updated": "2018-07-24T08:18:54.992Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "2f0c6b20-89dc-45fe-9ce7-4b4cd6b3dd03"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "a83083ae-6db0-4d3f-9dcb-c49e2a232696",
+    "fields": {
+        "created": "2018-07-24T08:18:55.062Z",
+        "updated": "2018-07-24T08:18:55.062Z",
+        "corpus": "test",
+        "type": "act",
+        "name": "Act 5",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "ab6fa45a-f1fc-4b95-a0fb-db59b5edd717",
+    "fields": {
+        "created": "2018-07-24T08:18:54.883Z",
+        "updated": "2018-07-24T08:18:54.883Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 1, page 1v",
+        "zone": "1dff74fb-a607-4118-a8d3-e1137d4fe122"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "ba22e17c-b93f-4683-b869-40588b483558",
+    "fields": {
+        "created": "2018-07-24T08:18:54.886Z",
+        "updated": "2018-07-24T08:18:54.886Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 1, page 2r",
+        "zone": "a8159284-10b7-423b-a199-8fe2abe949c8"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "bb32de06-561c-4c0d-94e0-c43720079cc3",
+    "fields": {
+        "created": "2018-07-24T08:18:55.017Z",
+        "updated": "2018-07-24T08:18:55.017Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "b01704a9-23ec-4a79-8ec3-476042697bb9"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "bc9f1cbd-0202-4075-9150-7e256bdbd485",
+    "fields": {
+        "created": "2018-07-24T08:18:55.117Z",
+        "updated": "2018-07-24T08:18:55.117Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface F",
+        "zone": "e5b7b231-2071-4521-a413-5cda61dcf909"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "c21ff1e2-694d-4310-97b6-d4a31cf0bfa3",
+    "fields": {
+        "created": "2018-07-24T08:18:54.879Z",
+        "updated": "2018-07-24T08:18:54.879Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 1, page 1r",
+        "zone": "26be1b53-bd37-4c2c-9cba-47c2c82403ea"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "c31fa532-b498-4b22-9b81-5110ef02971d",
+    "fields": {
+        "created": "2018-07-24T08:18:54.957Z",
+        "updated": "2018-07-24T08:18:54.957Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "286f8048-7b6e-4e4e-9664-22296303d47d"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "c6b3f72f-1373-45c7-a218-c9b0bcf1c70f",
+    "fields": {
+        "created": "2018-07-24T08:18:54.835Z",
+        "updated": "2018-07-24T08:18:54.835Z",
+        "corpus": "test",
+        "type": "register",
+        "name": "Register 1",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "c6d1ae4d-5bf1-4167-ae28-6518c1b572c2",
+    "fields": {
+        "created": "2018-07-24T08:18:55.004Z",
+        "updated": "2018-07-24T08:18:55.004Z",
+        "corpus": "test",
+        "type": "word",
+        "name": "",
+        "zone": "9833b904-5e2f-4642-a399-9c862068c734"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "ca3fa6e6-838b-4683-8cf9-f6ff75fa8e36",
+    "fields": {
+        "created": "2018-07-24T08:18:55.057Z",
+        "updated": "2018-07-24T08:18:55.057Z",
+        "corpus": "test",
+        "type": "act",
+        "name": "Act 3",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "ce80f464-5f47-4155-b596-bafe3465c102",
+    "fields": {
+        "created": "2018-07-24T08:18:54.919Z",
+        "updated": "2018-07-24T08:18:54.919Z",
+        "corpus": "test",
+        "type": "page",
+        "name": "Volume 2, page 2r",
+        "zone": "699eed11-e91a-448e-bd3e-4216765aa80e"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "cfd4952d-41ff-45f0-abe4-a904acf5350b",
+    "fields": {
+        "created": "2018-07-24T08:18:55.051Z",
+        "updated": "2018-07-24T08:18:55.051Z",
+        "corpus": "test",
+        "type": "act",
+        "name": "Act 1",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "f52d8907-aeac-45b1-b584-99c76a31f77b",
+    "fields": {
+        "created": "2018-07-24T08:18:55.112Z",
+        "updated": "2018-07-24T08:18:55.112Z",
+        "corpus": "test",
+        "type": "surface",
+        "name": "Surface D",
+        "zone": "87869157-a183-4015-839b-9fce89cce4f0"
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "fa275cab-d830-4f08-a227-6dd4b70ee377",
+    "fields": {
+        "created": "2018-07-24T08:18:54.826Z",
+        "updated": "2018-07-24T08:18:54.826Z",
+        "corpus": "test",
+        "type": "volume",
+        "name": "Volume 1",
+        "zone": null
+    }
+},
+{
+    "model": "documents.element",
+    "pk": "fd3ee4b9-edd2-4f82-9216-a1160bf890d4",
+    "fields": {
+        "created": "2018-07-24T08:18:55.054Z",
+        "updated": "2018-07-24T08:18:55.054Z",
+        "corpus": "test",
+        "type": "act",
+        "name": "Act 2",
+        "zone": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "6c17e8e6-71af-4a7d-8bdb-f21532fc024d",
+    "fields": {
+        "folio": "1v",
+        "page_type": "page",
+        "nb": 1,
+        "direction": "verso",
+        "complement": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "87f549c2-581d-4290-b641-75e73926968e",
+    "fields": {
+        "folio": "1r",
+        "page_type": "page",
+        "nb": 1,
+        "direction": "recto",
+        "complement": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "ab6fa45a-f1fc-4b95-a0fb-db59b5edd717",
+    "fields": {
+        "folio": "1v",
+        "page_type": "page",
+        "nb": 1,
+        "direction": "verso",
+        "complement": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "ba22e17c-b93f-4683-b869-40588b483558",
+    "fields": {
+        "folio": "2r",
+        "page_type": "page",
+        "nb": 2,
+        "direction": "recto",
+        "complement": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "c21ff1e2-694d-4310-97b6-d4a31cf0bfa3",
+    "fields": {
+        "folio": "1r",
+        "page_type": "page",
+        "nb": 1,
+        "direction": "recto",
+        "complement": null
+    }
+},
+{
+    "model": "documents.page",
+    "pk": "ce80f464-5f47-4155-b596-bafe3465c102",
+    "fields": {
+        "folio": "2r",
+        "page_type": "page",
+        "nb": 2,
+        "direction": "recto",
+        "complement": null
+    }
+},
+{
+    "model": "documents.act",
+    "pk": "3901fbac-ab69-4fa5-9e8e-e973ec684873",
+    "fields": {
+        "number": "4",
+        "folio": "2r"
+    }
+},
+{
+    "model": "documents.act",
+    "pk": "a83083ae-6db0-4d3f-9dcb-c49e2a232696",
+    "fields": {
+        "number": "5",
+        "folio": "2r"
+    }
+},
+{
+    "model": "documents.act",
+    "pk": "ca3fa6e6-838b-4683-8cf9-f6ff75fa8e36",
+    "fields": {
+        "number": "3",
+        "folio": "2r"
+    }
+},
+{
+    "model": "documents.act",
+    "pk": "cfd4952d-41ff-45f0-abe4-a904acf5350b",
+    "fields": {
+        "number": "1",
+        "folio": "1r"
+    }
+},
+{
+    "model": "documents.act",
+    "pk": "fd3ee4b9-edd2-4f82-9216-a1160bf890d4",
+    "fields": {
+        "number": "2",
+        "folio": "1r-1v"
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "1327dc18-e5c7-44b0-91e6-9dfee3417ce9",
+    "fields": {
+        "line": null,
+        "text": "PARIS",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "58343ea9-176f-434c-a9ed-22f975b79968",
+    "fields": {
+        "line": null,
+        "text": "DATUM",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "59741867-4b09-4874-afab-86f931d2e223",
+    "fields": {
+        "line": null,
+        "text": "PARIS",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "6bf20744-97fa-4372-bcc0-5458bb5de3ce",
+    "fields": {
+        "line": null,
+        "text": "DATUM",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "8069f69b-3f46-47bf-9492-4aeaca1d3a50",
+    "fields": {
+        "line": null,
+        "text": "ROY",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "9ef69659-7f7c-4204-9ceb-dbdc2d8daec3",
+    "fields": {
+        "line": null,
+        "text": "ROY",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "bb32de06-561c-4c0d-94e0-c43720079cc3",
+    "fields": {
+        "line": null,
+        "text": "PARIS",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "c31fa532-b498-4b22-9b81-5110ef02971d",
+    "fields": {
+        "line": null,
+        "text": "ROY",
+        "score": 1.0
+    }
+},
+{
+    "model": "documents.transcription",
+    "pk": "c6d1ae4d-5bf1-4167-ae28-6518c1b572c2",
+    "fields": {
+        "line": null,
+        "text": "DATUM",
+        "score": 1.0
+    }
+},
+{
+    "model": "images.imageserver",
+    "pk": 1,
+    "fields": {
+        "name": "Test Server",
+        "url": "http://server",
+        "created": "2018-07-24T08:18:54.795Z",
+        "updated": "2018-07-24T08:18:54.795Z"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "195299b4-da0c-4499-a67c-7f926e63116f",
+    "fields": {
+        "created": "2018-07-24T08:18:54.800Z",
+        "updated": "2018-07-24T08:18:54.800Z",
+        "server": 1,
+        "path": "img2",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "2feedc50-07d5-41bd-b720-ba2a2ce85bb2",
+    "fields": {
+        "created": "2018-07-24T08:18:54.806Z",
+        "updated": "2018-07-24T08:18:54.806Z",
+        "server": 1,
+        "path": "img5",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "37762fff-fc51-4880-b1e0-5c7470e4a54b",
+    "fields": {
+        "created": "2018-07-24T08:18:54.804Z",
+        "updated": "2018-07-24T08:18:54.804Z",
+        "server": 1,
+        "path": "img4",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+    "fields": {
+        "created": "2018-07-24T08:18:54.798Z",
+        "updated": "2018-07-24T08:18:54.798Z",
+        "server": 1,
+        "path": "img1",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+    "fields": {
+        "created": "2018-07-24T08:18:54.802Z",
+        "updated": "2018-07-24T08:18:54.802Z",
+        "server": 1,
+        "path": "img3",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.image",
+    "pk": "f298ee15-f126-44bc-ae22-459d18537e6f",
+    "fields": {
+        "created": "2018-07-24T08:18:54.808Z",
+        "updated": "2018-07-24T08:18:54.808Z",
+        "server": 1,
+        "path": "img6",
+        "width": 1000,
+        "height": 1000,
+        "status": "unchecked"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "0554b559-3f85-40d7-b531-9c4a159e06e2",
+    "fields": {
+        "created": "2018-07-24T08:18:54.817Z",
+        "updated": "2018-07-24T08:18:54.817Z",
+        "image": "37762fff-fc51-4880-b1e0-5c7470e4a54b",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "1542f839-401b-4cf7-8ef1-afc02f6f3cc1",
+    "fields": {
+        "created": "2018-07-24T08:18:54.819Z",
+        "updated": "2018-07-24T08:18:54.819Z",
+        "image": "2feedc50-07d5-41bd-b720-ba2a2ce85bb2",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "1dff74fb-a607-4118-a8d3-e1137d4fe122",
+    "fields": {
+        "created": "2018-07-24T08:18:54.813Z",
+        "updated": "2018-07-24T08:18:54.813Z",
+        "image": "195299b4-da0c-4499-a67c-7f926e63116f",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "26be1b53-bd37-4c2c-9cba-47c2c82403ea",
+    "fields": {
+        "created": "2018-07-24T08:18:54.810Z",
+        "updated": "2018-07-24T08:18:54.810Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "26eb4783-b8c9-4768-b55f-0f61f3512659",
+    "fields": {
+        "created": "2018-07-24T08:18:55.105Z",
+        "updated": "2018-07-24T08:18:55.105Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((600,600), (1000,600), (1000,1000), (600,1000), (600,600))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "286f8048-7b6e-4e4e-9664-22296303d47d",
+    "fields": {
+        "created": "2018-07-24T08:18:54.955Z",
+        "updated": "2018-07-24T08:18:54.955Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((400,400), (500,400), (500,500), (400,500), (400,400))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "2f0c6b20-89dc-45fe-9ce7-4b4cd6b3dd03",
+    "fields": {
+        "created": "2018-07-24T08:18:54.990Z",
+        "updated": "2018-07-24T08:18:54.990Z",
+        "image": "195299b4-da0c-4499-a67c-7f926e63116f",
+        "polygon": "Polygon((400,400), (500,400), (500,500), (400,500), (400,400))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "699eed11-e91a-448e-bd3e-4216765aa80e",
+    "fields": {
+        "created": "2018-07-24T08:18:54.821Z",
+        "updated": "2018-07-24T08:18:54.821Z",
+        "image": "f298ee15-f126-44bc-ae22-459d18537e6f",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "87869157-a183-4015-839b-9fce89cce4f0",
+    "fields": {
+        "created": "2018-07-24T08:18:55.110Z",
+        "updated": "2018-07-24T08:18:55.110Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((0,0), (300,0), (300,300), (0,300), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "8b777c07-2cf5-4b47-b407-a19dfc27fcc9",
+    "fields": {
+        "created": "2018-07-24T08:18:55.113Z",
+        "updated": "2018-07-24T08:18:55.113Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((300,300), (600,300), (600,600), (300,600), (300,300))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "9833b904-5e2f-4642-a399-9c862068c734",
+    "fields": {
+        "created": "2018-07-24T08:18:55.001Z",
+        "updated": "2018-07-24T08:18:55.002Z",
+        "image": "195299b4-da0c-4499-a67c-7f926e63116f",
+        "polygon": "Polygon((700,700), (800,700), (800,800), (700,800), (700,700))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "a8159284-10b7-423b-a199-8fe2abe949c8",
+    "fields": {
+        "created": "2018-07-24T08:18:54.815Z",
+        "updated": "2018-07-24T08:18:54.815Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "af98c80b-e1b5-4b05-9771-d28e9e543c85",
+    "fields": {
+        "created": "2018-07-24T08:18:54.966Z",
+        "updated": "2018-07-24T08:18:54.966Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((700,700), (800,700), (800,800), (700,800), (700,700))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "b01704a9-23ec-4a79-8ec3-476042697bb9",
+    "fields": {
+        "created": "2018-07-24T08:18:55.014Z",
+        "updated": "2018-07-24T08:18:55.014Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((100,100), (200,100), (200,200), (100,200), (100,100))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "b968bc1a-2c25-47e3-b49b-d6a05dc6d245",
+    "fields": {
+        "created": "2018-07-24T08:18:55.101Z",
+        "updated": "2018-07-24T08:18:55.101Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((0,0), (600,0), (600,600), (0,600), (0,0))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "c50126f5-682d-42ed-ab2d-f359717b81ad",
+    "fields": {
+        "created": "2018-07-24T08:18:55.027Z",
+        "updated": "2018-07-24T08:18:55.027Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((400,400), (500,400), (500,500), (400,500), (400,400))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "c58b425e-1bdf-4c0d-860c-28e6793db75f",
+    "fields": {
+        "created": "2018-07-24T08:18:55.038Z",
+        "updated": "2018-07-24T08:18:55.038Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((700,700), (800,700), (800,800), (700,800), (700,700))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "cc592547-529e-4289-8351-53fe6a5cc55c",
+    "fields": {
+        "created": "2018-07-24T08:18:54.943Z",
+        "updated": "2018-07-24T08:18:54.943Z",
+        "image": "42d18a7a-65d9-4b52-b42a-ed0b6c07bceb",
+        "polygon": "Polygon((100,100), (200,100), (200,200), (100,200), (100,100))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "d94e1f63-584d-423f-9d66-966362e413a5",
+    "fields": {
+        "created": "2018-07-24T08:18:54.978Z",
+        "updated": "2018-07-24T08:18:54.978Z",
+        "image": "195299b4-da0c-4499-a67c-7f926e63116f",
+        "polygon": "Polygon((100,100), (200,100), (200,200), (100,200), (100,100))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "e5b7b231-2071-4521-a413-5cda61dcf909",
+    "fields": {
+        "created": "2018-07-24T08:18:55.116Z",
+        "updated": "2018-07-24T08:18:55.116Z",
+        "image": "c01f2ae6-abea-4a21-847f-6de9919cb91d",
+        "polygon": "Polygon((600,600), (1000,600), (1000,1000), (600,1000), (600,600))"
+    }
+},
+{
+    "model": "images.zone",
+    "pk": "ee7b9b2b-ee1d-49fc-8192-a03c16a03364",
+    "fields": {
+        "created": "2018-07-24T08:18:55.107Z",
+        "updated": "2018-07-24T08:18:55.107Z",
+        "image": "195299b4-da0c-4499-a67c-7f926e63116f",
+        "polygon": "Polygon((0,0), (1000,0), (1000,1000), (0,1000), (0,0))"
+    }
+},
+{
+    "model": "users.user",
+    "pk": 1,
+    "fields": {
+        "password": "pbkdf2_sha256$100000$DcHO7CYPnlU5$gFLvCPAoUpyDhb785RnLm/VsgHhB523vxLvxsbF7W6k=",
+        "last_login": null,
+        "email": "root@root.fr",
+        "is_active": true,
+        "is_admin": true
+    }
+},
+{
+    "model": "users.user",
+    "pk": 2,
+    "fields": {
+        "password": "pbkdf2_sha256$100000$shjhRsQTakNR$Zbsbhx//3bR0juLcxyJF0+jbUC1+1+U6DN6HznIqTBc=",
+        "last_login": null,
+        "email": "user@user.fr",
+        "is_active": true,
+        "is_admin": false
+    }
+}
+]
diff --git a/arkindex/documents/management/commands/build_fixtures.py b/arkindex/documents/management/commands/build_fixtures.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc4251c5773452915b9910b58701143a8a8db898
--- /dev/null
+++ b/arkindex/documents/management/commands/build_fixtures.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+from django.core.management.base import BaseCommand
+from arkindex.documents.models import Corpus, Element, ElementType, Page, PageDirection, PageType, Act, Transcription
+from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.users.models import User
+
+
+def makezone(img, x1, x2):
+    """
+    Shortcut to create a square zone on an image
+    """
+    return Zone.objects.create(polygon=[(x1, x1), (x2, x1), (x2, x2), (x1, x2), (x1, x1)], image=img)
+
+
+class Command(BaseCommand):
+    help = 'Create test objects in the database'
+
+    def handle(self, *args, **options):
+        # Create 1 image server
+        imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
+
+        # Create 6 images
+        img1 = Image.objects.create(path='img1', width=1000, height=1000, server=imgsrv)
+        img2 = Image.objects.create(path='img2', width=1000, height=1000, server=imgsrv)
+        img3 = Image.objects.create(path='img3', width=1000, height=1000, server=imgsrv)
+        img4 = Image.objects.create(path='img4', width=1000, height=1000, server=imgsrv)
+        img5 = Image.objects.create(path='img5', width=1000, height=1000, server=imgsrv)
+        img6 = Image.objects.create(path='img6', width=1000, height=1000, server=imgsrv)
+
+        # Create 6 zones on the images for pages
+        z1 = makezone(img1, 0, 1000)
+        z2 = makezone(img2, 0, 1000)
+        z3 = makezone(img3, 0, 1000)
+        z4 = makezone(img4, 0, 1000)
+        z5 = makezone(img5, 0, 1000)
+        z6 = makezone(img6, 0, 1000)
+
+        # Create 1 corpus
+        corpus = Corpus.objects.create(id='test', name='Unit Tests')
+
+        # Create 2 volumes
+        vol1 = Element.objects.create(corpus=corpus, type=ElementType.Volume, name="Volume 1")
+        vol2 = Element.objects.create(corpus=corpus, type=ElementType.Volume, name="Volume 2")
+
+        # Create registers for each volume
+        reg1 = Element.objects.create(corpus=corpus, type=ElementType.Register, name="Register 1")
+        reg2 = Element.objects.create(corpus=corpus, type=ElementType.Register, name="Register 2")
+        reg1.add_parent(vol1)
+        reg2.add_parent(vol2)
+
+        # Create 3 pages in each volume
+        p1_1 = Page.objects.create(
+            corpus=corpus, name="Volume 1, page 1r", folio="1r", page_type=PageType.Page,
+            nb=1, direction=PageDirection.Recto, zone=z1)
+        p1_2 = Page.objects.create(
+            corpus=corpus, name="Volume 1, page 1v", folio="1v", page_type=PageType.Page,
+            nb=1, direction=PageDirection.Verso, zone=z2)
+        p1_3 = Page.objects.create(
+            corpus=corpus, name="Volume 1, page 2r", folio="2r", page_type=PageType.Page,
+            nb=2, direction=PageDirection.Recto, zone=z3)
+        p1_1.add_parent(reg1)
+        p1_2.add_parent(reg1)
+        p1_3.add_parent(reg1)
+
+        p2_1 = Page.objects.create(
+            corpus=corpus, name="Volume 2, page 1r", folio="1r", page_type=PageType.Page,
+            nb=1, direction=PageDirection.Recto, zone=z4)
+        p2_2 = Page.objects.create(
+            corpus=corpus, name="Volume 2, page 1v", folio="1v", page_type=PageType.Page,
+            nb=1, direction=PageDirection.Verso, zone=z5)
+        p2_3 = Page.objects.create(
+            corpus=corpus, name="Volume 2, page 2r", folio="2r", page_type=PageType.Page,
+            nb=2, direction=PageDirection.Recto, zone=z6)
+        p2_1.add_parent(reg2)
+        p2_2.add_parent(reg2)
+        p2_3.add_parent(reg2)
+
+        # Create transcriptions on images of volume 1
+        for page in (p1_1, p1_2, p1_3):
+            for word, pos in [("PARIS", 100), ("ROY", 400), ("DATUM", 700)]:
+                Transcription.objects.create(
+                    corpus=corpus,
+                    text=word,
+                    type=ElementType.Word,
+                    zone=makezone(page.zone.image, pos, pos + 100),
+                    score=1.0,
+                ).add_parent(page)
+
+        # Create 5 acts on volume 1
+        act1 = Act.objects.create(corpus=corpus, name="Act 1", number="1", folio="1r")
+        act2 = Act.objects.create(corpus=corpus, name="Act 2", number="2", folio="1r-1v")
+        act3 = Act.objects.create(corpus=corpus, name="Act 3", number="3", folio="2r")
+        act4 = Act.objects.create(corpus=corpus, name="Act 4", number="4", folio="2r")
+        act5 = Act.objects.create(corpus=corpus, name="Act 5", number="5", folio="2r")
+        act1.add_parent(reg1)
+        act2.add_parent(reg1)
+        act3.add_parent(reg1)
+        act4.add_parent(reg1)
+        act5.add_parent(reg1)
+
+        # Create surfaces on the 3 pages of volume 1
+        # Page 1 [A B] Page 2 [C] Page 3 [D E F]
+        sa = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface A", zone=makezone(img1, 0, 600))
+        sb = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface B", zone=makezone(img1, 600, 1000))
+        sc = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface C", zone=makezone(img2, 0, 1000))
+        sd = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface D", zone=makezone(img3, 0, 300))
+        se = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface E", zone=makezone(img3, 300, 600))
+        sf = Element.objects.create(corpus=corpus, type=ElementType.Surface,
+                                    name="Surface F", zone=makezone(img3, 600, 1000))
+        # Act 1 [A] Act 2 [B C] Act 3 [D] Act 4 [E] Act 5 [F]
+        sa.add_parent(act1)
+        sb.add_parent(act2)
+        sc.add_parent(act2)
+        sd.add_parent(act3)
+        se.add_parent(act4)
+        sf.add_parent(act5)
+
+        # Create an admin and a user
+        User.objects.create_superuser('root@root.fr', 'Pa$$w0rd')
+        User.objects.create_user('user@user.fr', 'Pa$$w0rd')
diff --git a/arkindex/documents/tests/test_act.py b/arkindex/documents/tests/test_act.py
index 3956b319cc3df99e073eb462501ebe5e4cc0ddb2..d8cdfaece938a497280c50ce1d869e955d0e4604 100644
--- a/arkindex/documents/tests/test_act.py
+++ b/arkindex/documents/tests/test_act.py
@@ -1,17 +1,15 @@
 from django.urls import reverse
-from rest_framework.test import APITestCase
+from arkindex.project.tests import FixtureAPITestCase
 from rest_framework import status
-from arkindex.documents.models import Corpus, Element, Act, ElementType, MetaData, MetaType
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.documents.models import Element, Act, ElementType, MetaData, MetaType
+from arkindex.images.models import Image, Zone
 
 
-class TestAct(APITestCase):
+class TestAct(FixtureAPITestCase):
 
     def setUp(self):
         # Create an act with some surfaces
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img = Image.objects.create(path='img', width=1337, height=42, server=self.imgsrv)
+        self.img = Image.objects.get(path='img6', server=self.imgsrv)
         self.act = Act.objects.create(
             corpus=self.corpus,
             name='sister act',
diff --git a/arkindex/documents/tests/test_annotation_list.py b/arkindex/documents/tests/test_annotation_list.py
index e1e47a62e99e537e7f3bf76b186a169044e641f0..9311410003493ffc60bae6783a993f22a13dd430 100644
--- a/arkindex/documents/tests/test_annotation_list.py
+++ b/arkindex/documents/tests/test_annotation_list.py
@@ -1,30 +1,14 @@
 from django.urls import reverse
-from rest_framework.test import APITestCase
+from arkindex.project.tests import FixtureAPITestCase
 from rest_framework import status
-from arkindex.documents.models import Corpus, Element, Page, Transcription, Act, ElementType
-from arkindex.images.models import ImageServer, Image, Zone
-
-
-class TestPageAnnotationListSerializer(APITestCase):
-
-    def setUp(self):
-        # Create a page and an image with some transcriptions
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img = Image.objects.create(path='img', width=1337, height=42, server=self.imgsrv)
-        pagezone = Zone.objects.create(polygon=[(0, 0), (1337, 0), (1337, 42), (42, 0), (0, 0)], image=self.img)
-        self.page = Page.objects.create(corpus=self.corpus, name="page", folio="page", zone=pagezone)
-        self.z1 = Zone.objects.create(
-            polygon=[(100, 200), (100, 300), (300, 300), (300, 200), (100, 200)], image=self.img)
-        self.z2 = Zone.objects.create(
-            polygon=[(50, 100), (50, 150), (150, 150), (150, 100), (50, 100)], image=self.img)
-        self.t1 = Transcription.objects.create(type=ElementType.Word, corpus=self.corpus, text="AAA", zone=self.z1)
-        self.t2 = Transcription.objects.create(type=ElementType.Word, corpus=self.corpus, text="BBB", zone=self.z2)
-        self.t1.add_parent(self.page)
-        self.t2.add_parent(self.page)
+from arkindex.documents.models import Page
+
+
+class TestPageAnnotationListSerializer(FixtureAPITestCase):
 
     def test_normal_list(self):
-        response = self.client.get(reverse('api:page-transcription-manifest', kwargs={'pk': self.page.id}))
+        page = Page.objects.get(corpus=self.corpus, zone__image__path='img1')
+        response = self.client.get(reverse('api:page-transcription-manifest', kwargs={'pk': page.id}))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         annotation_list = response.json()
 
@@ -35,7 +19,7 @@ class TestPageAnnotationListSerializer(APITestCase):
 
         self.assertEqual(annotation_list['@type'], 'sc:AnnotationList')
         self.assertEqual(annotation_list['@context'], 'http://iiif.io/api/presentation/2/context.json')
-        self.assertEqual(len(annotation_list['resources']), 2)
+        self.assertEqual(len(annotation_list['resources']), 3)
 
         for annotation in annotation_list['resources']:
             self.assertIn('@type', annotation)
@@ -55,12 +39,12 @@ class TestPageAnnotationListSerializer(APITestCase):
 
             self.assertEqual(resource['format'], 'text/plain')
             self.assertEqual(resource['@type'], 'cnt:ContentAsText')
-            self.assertIn(resource['chars'], ['AAA', 'BBB'])
+            self.assertIn(resource['chars'], ['PARIS', 'ROY', 'DATUM'])
 
     def test_empty_list(self):
         # An annotation list with nothing in it
         response = self.client.get(reverse('api:page-transcription-manifest', kwargs={
-            'pk': Page.objects.create(corpus=self.corpus, name="Empty Page").id
+            'pk': Page.objects.get(corpus=self.corpus, zone__image__path='img6').id
         }))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         annotation_list = response.json()
@@ -68,26 +52,11 @@ class TestPageAnnotationListSerializer(APITestCase):
         self.assertEqual(len(annotation_list['resources']), 0)
 
 
-class TestPageActAnnotationListSerializer(APITestCase):
-
-    def setUp(self):
-        # Create a page and an image with some transcriptions
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        img = Image.objects.create(path='img', width=1337, height=42, server=imgsrv)
-        pagezone = Zone.objects.create(polygon=[(0, 0), (1337, 0), (1337, 42), (42, 0), (0, 0)], image=img)
-        self.page = Page.objects.create(corpus=self.corpus, name="page", folio="page", zone=pagezone)
-        z1 = Zone.objects.create(polygon=[(100, 200), (100, 300), (300, 300), (300, 200), (100, 200)], image=img)
-        z2 = Zone.objects.create(polygon=[(50, 100), (50, 150), (150, 150), (150, 100), (50, 100)], image=img)
-        a1 = Act.objects.create(corpus=self.corpus, name="a1", number="123")
-        a2 = Act.objects.create(corpus=self.corpus, name="a2", number="456")
-        s1 = Element.objects.create(corpus=self.corpus, type=ElementType.Surface, name="s1", zone=z1)
-        s2 = Element.objects.create(corpus=self.corpus, type=ElementType.Surface, name="s2", zone=z2)
-        s1.add_parent(a1)
-        s2.add_parent(a2)
+class TestPageActAnnotationListSerializer(FixtureAPITestCase):
 
     def test_normal_list(self):
-        response = self.client.get(reverse('api:page-act-manifest', kwargs={'pk': self.page.id}))
+        page = Page.objects.get(corpus=self.corpus, zone__image__path='img1')
+        response = self.client.get(reverse('api:page-act-manifest', kwargs={'pk': page.id}))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         annotation_list = response.json()
 
@@ -118,12 +87,12 @@ class TestPageActAnnotationListSerializer(APITestCase):
 
             self.assertEqual(resource['format'], 'text/plain')
             self.assertEqual(resource['@type'], 'cnt:ContentAsText')
-            self.assertIn(resource['chars'], ['Act 123', 'Act 456'])
+            self.assertIn(resource['chars'], ['Act 1', 'Act 2'])
 
     def test_empty_list(self):
         # An annotation list with nothing in it
         response = self.client.get(reverse('api:page-act-manifest', kwargs={
-            'pk': Page.objects.create(corpus=self.corpus, name="Empty Page").id
+            'pk': Page.objects.get(corpus=self.corpus, zone__image__path='img6').id
         }))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         annotation_list = response.json()
diff --git a/arkindex/documents/tests/test_edit_elementpath.py b/arkindex/documents/tests/test_edit_elementpath.py
index b5329bc697cf44f55ef0ece5c0dee6a329adc8d5..b43b171151d5dcd11c9b6b542120c4c9333e1fb2 100644
--- a/arkindex/documents/tests/test_edit_elementpath.py
+++ b/arkindex/documents/tests/test_edit_elementpath.py
@@ -1,14 +1,12 @@
-from django.test import TestCase
-from arkindex.documents.models import Corpus, Element, ElementPath, ElementType
+from arkindex.project.tests import FixtureTestCase
+from arkindex.documents.models import Element, ElementPath, ElementType
 import itertools
 
 
-class TestEditElementPath(TestCase):
+class TestEditElementPath(FixtureTestCase):
     """
     Test ElementPath editing algorithms
     """
-    def setUp(self):
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
 
     def build_tree(self, tree, element_type=ElementType.Volume):
         """
diff --git a/arkindex/documents/tests/test_element_manager.py b/arkindex/documents/tests/test_element_manager.py
index 534d6854fb7e719e1a5ebe25a291a8bbf7024e58..07c049319ab1dc148d907ff94f6590d7e663c1a1 100644
--- a/arkindex/documents/tests/test_element_manager.py
+++ b/arkindex/documents/tests/test_element_manager.py
@@ -1,21 +1,17 @@
-from django.test import TestCase
-from arkindex.documents.models import Corpus, Element, ElementType
+from arkindex.project.tests import FixtureTestCase
+from arkindex.documents.models import Element, ElementType
 
 
-class TestElementManager(TestCase):
+class TestElementManager(FixtureTestCase):
     """Tests for ElementManager class"""
 
     def setUp(self):
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.reg = Element.objects.create(corpus=self.corpus, name="Register", type=ElementType.Register)
-        self.vol = Element.objects.create(corpus=self.corpus, name="Volume", type=ElementType.Volume)
-        self.p1 = Element.objects.create(corpus=self.corpus, name="Page1", type=ElementType.Page)
-        self.p2 = Element.objects.create(corpus=self.corpus, name="Page2", type=ElementType.Page)
+        self.vol = Element.objects.get(name="Volume 2")
+        self.reg = Element.objects.get(name="Register 2")
+        self.p1 = Element.objects.get(name="Volume 2, page 1r")
+        self.p2 = Element.objects.get(name="Volume 2, page 1v")
+        self.p3 = Element.objects.get(name="Volume 2, page 2r")
         self.act = Element.objects.create(corpus=self.corpus, name="Act", type=ElementType.Act)
-        # Register --> Volume --> [Page1 --> Act, Page2]
-        self.vol.add_parent(self.reg)
-        self.p1.add_parent(self.vol)
-        self.p2.add_parent(self.vol)
         self.act.add_parent(self.p1)
 
     def test_get_ascending(self):
@@ -25,33 +21,35 @@ class TestElementManager(TestCase):
 
     def test_get_ascending_empty(self):
         # Use Register, expect empty list
-        ids = Element.objects.get_ascending(self.reg.id)
+        ids = Element.objects.get_ascending(self.vol.id)
         self.assertCountEqual(ids, [])
 
     def test_get_ascendings(self):
-        # Use Page1 and Page2, expect Register and Volume twice
-        ids = Element.objects.get_ascendings(self.p1.id, self.p2.id)
+        # Use all three pages, expect Register and Volume thrice
+        ids = Element.objects.get_ascendings(self.p1.id, self.p2.id, self.p3.id)
         self.assertCountEqual(ids[self.p1.id], [self.reg, self.vol])
         self.assertCountEqual(ids[self.p2.id], [self.reg, self.vol])
+        self.assertCountEqual(ids[self.p3.id], [self.reg, self.vol])
 
     def test_get_descending(self):
-        # Use Volume, expect Page1, Page2 and Act
-        ids = Element.objects.get_descending(self.vol.id)
-        self.assertCountEqual(ids, [self.p1, self.p2, self.act])
+        # Use register, expect all three pages and the act
+        ids = Element.objects.get_descending(self.reg.id)
+        self.assertCountEqual(ids, [self.p1, self.p2, self.p3, self.act])
 
     def test_get_descending_empty(self):
-        # Use Page2, expect empty list
+        # Use page 2, expect empty list
         ids = Element.objects.get_descending(self.p2.id)
         self.assertCountEqual(ids, [])
 
     def test_get_descendings(self):
-        # Use Page1 and Page2, expect Act and nothing else
-        ids = Element.objects.get_descendings([self.p1.id, self.p2.id])
+        # Use all pages, expect Act and nothing else
+        ids = Element.objects.get_descendings([self.p1.id, self.p2.id, self.p3.id])
         self.assertCountEqual(ids[self.p1.id], [self.act])
         self.assertCountEqual(ids[self.p2.id], [])
+        self.assertCountEqual(ids[self.p3.id], [])
 
     def test_get_related(self):
-        # Use Page1, expect Register, Volume and Act
+        # Use page 1, expect Register, Volume and Act
         ids = Element.objects.get_related(self.p1.id)
         self.assertCountEqual(ids, [self.reg, self.vol, self.act])
 
@@ -64,4 +62,4 @@ class TestElementManager(TestCase):
     def test_get_ascending_paths(self):
         paths = Element.objects.get_ascending_paths(self.act.id)
         self.assertEqual(len(paths), 1)
-        self.assertSequenceEqual(list(paths[0]), [self.reg, self.vol, self.p1])
+        self.assertSequenceEqual(list(paths[0]), [self.vol, self.reg, self.p1])
diff --git a/arkindex/documents/tests/test_search_post.py b/arkindex/documents/tests/test_search_post.py
index 0ce776b835dcc8e112846e6a38c7d7149535ab22..56f16d44b66047ffe2d40fa6af943a4ea4c0725e 100644
--- a/arkindex/documents/tests/test_search_post.py
+++ b/arkindex/documents/tests/test_search_post.py
@@ -1,95 +1,11 @@
-from django.test import TestCase
-from arkindex.documents.models import Corpus, Element, ElementType, Transcription, Act, Page
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.project.tests import FixtureTestCase
+from arkindex.documents.models import Transcription, Act, Element
 from arkindex.documents.search import search_transcriptions_post, search_acts_post, search_transcriptions_filter_post
 
 
-class TestSearchPostProcess(TestCase):
+class TestSearchPostProcess(FixtureTestCase):
     """Test ElasticSearch post-processing functions"""
 
-    def setUp(self):
-        # Create a server with three images
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img1 = Image.objects.create(path='img1', width=1000, height=1000, server=self.imgsrv)
-        self.img2 = Image.objects.create(path='img2', width=1000, height=1000, server=self.imgsrv)
-        self.img3 = Image.objects.create(path='img3', width=1000, height=1000, server=self.imgsrv)
-        self.z1 = Zone.objects.create(polygon=[(0, 0), (1000, 0), (1000, 1000), (1000, 0), (0, 0)], image=self.img1)
-        self.z2 = Zone.objects.create(polygon=[(0, 0), (1000, 0), (1000, 1000), (1000, 0), (0, 0)], image=self.img2)
-        self.z3 = Zone.objects.create(polygon=[(0, 0), (1000, 0), (1000, 1000), (1000, 0), (0, 0)], image=self.img3)
-
-        # Create a volume with two pages and another with one page
-        self.vol1 = Element.objects.create(corpus=self.corpus, name="Volume 1", type=ElementType.Volume)
-        self.vol2 = Element.objects.create(corpus=self.corpus, name="Volume 2", type=ElementType.Volume)
-        self.p1 = Page.objects.create(corpus=self.corpus, name="p1", folio="p1", zone=self.z1)
-        self.p2 = Page.objects.create(corpus=self.corpus, name="p2", folio="p2", zone=self.z2)
-        self.p3 = Page.objects.create(corpus=self.corpus, name="p3", folio="p3", zone=self.z3)
-        self.p1.add_parent(self.vol1)
-        self.p2.add_parent(self.vol1)
-        self.p3.add_parent(self.vol2)
-
-        # Create a bunch of transcriptions
-        self.t1 = Transcription.objects.create(
-            corpus=self.corpus,
-            text="word",
-            type=ElementType.Word,
-            zone=Zone.objects.create(
-                polygon=[(10, 10), (20, 10), (20, 20), (10, 20), (10, 10)],
-                image=self.img1,
-            )
-        )
-        self.t2 = Transcription.objects.create(
-            corpus=self.corpus,
-            text="word",
-            type=ElementType.Word,
-            zone=Zone.objects.create(
-                polygon=[(110, 110), (120, 110), (120, 120), (110, 120), (110, 110)],
-                image=self.img1,
-            )
-        )
-        self.t3 = Transcription.objects.create(
-            corpus=self.corpus,
-            text="word",
-            type=ElementType.Word,
-            zone=Zone.objects.create(
-                polygon=[(210, 210), (220, 210), (220, 220), (210, 220), (210, 210)],
-                image=self.img2,
-            )
-        )
-        self.t4 = Transcription.objects.create(
-            corpus=self.corpus,
-            text="word",
-            type=ElementType.Word,
-            zone=Zone.objects.create(
-                polygon=[(310, 210), (320, 310), (320, 320), (310, 320), (310, 310)],
-                image=self.img3,
-            )
-        )
-        self.t1.add_parent(self.p1)
-        self.t2.add_parent(self.p1)
-        self.t3.add_parent(self.p2)
-        self.t4.add_parent(self.p3)
-
-        # Create an act with surfaces on the first volume
-        self.act = Act.objects.create(corpus=self.corpus, name="Act 42", number="42")
-        self.sz1 = Zone.objects.create(polygon=[(0, 0), (500, 0), (500, 500), (0, 500), (0, 0)], image=self.img1)
-        self.sz2 = Zone.objects.create(polygon=[(0, 0), (500, 0), (500, 500), (0, 500), (0, 0)], image=self.img2)
-        self.surf1 = Element.objects.create(
-            corpus=self.corpus,
-            type=ElementType.Surface,
-            name="Surface 1",
-            zone=self.sz1,
-        )
-        self.surf2 = Element.objects.create(
-            corpus=self.corpus,
-            type=ElementType.Surface,
-            name="Surface 2",
-            zone=self.sz2,
-        )
-        self.act.add_parent(self.vol1)
-        self.surf1.add_parent(self.act)
-        self.surf2.add_parent(self.act)
-
     def build_es_response(self, hits):
         return {
             "hits": {
@@ -148,7 +64,7 @@ class TestSearchPostProcess(TestCase):
                 "transcriptions": {
                     "hits": {
                         "total": len(ts),
-                        "hits": [self.make_nested_transcription_hit(t) for t in ts],
+                        "hits": list(map(self.make_nested_transcription_hit, ts)),
                         "max_score": 1337,
                     }
                 }
@@ -156,28 +72,31 @@ class TestSearchPostProcess(TestCase):
         }
 
     def test_search_transcriptions_post(self):
-        expected = (self.t1, self.t2, self.t3)
+        expected = Transcription.objects.filter(text="PARIS")
         results = search_transcriptions_post(self.build_es_response(
-            [self.make_transcription_hit(ts) for ts in expected]
+            list(map(self.make_transcription_hit, expected))
         ))
         self.assertCountEqual(results, expected)
 
     def test_search_acts_post(self):
+        act = Act.objects.get(number="1")
+        ts = Transcription.objects.filter(text__in=["PARIS", "ROY"], zone__image__path='img1')
+        surf = Element.objects.get(name="Surface A").zone
         results = search_acts_post(self.build_es_response(
-            [self.make_act_hit(self.act, (self.t1, self.t2, self.t3)), ]
+            [self.make_act_hit(act, ts), ]
         ))
 
-        self.assertCountEqual(results, [self.act])
+        self.assertCountEqual(results, [act])
         self.assertTrue(hasattr(results[0], 'transcriptions'))
         self.assertTrue(hasattr(results[0], 'surfaces'))
-        self.assertCountEqual(results[0].transcriptions, [self.t1, self.t2, self.t3])
-        self.assertCountEqual(results[0].surfaces, [self.sz1, self.sz2])
+        self.assertCountEqual(results[0].transcriptions, ts)
+        self.assertCountEqual(results[0].surfaces, [surf])
 
     def test_search_transcriptions_filter_post(self):
         # Filter to only get transcriptions from volume 1
-        unfiltered = (self.t1, self.t2, self.t3, self.t4)
-        expected = (self.t1, self.t2, self.t3)
+        unfiltered = Transcription.objects.filter(text="PARIS")
+        expected = Transcription.objects.filter(text="PARIS", zone__image__path__in=['img1', 'img2', 'img3'])
         results = search_transcriptions_filter_post(self.build_es_response(
-            [self.make_transcription_hit(ts) for ts in unfiltered]
-        ), self.vol1.id)
+            list(map(self.make_transcription_hit, unfiltered))
+        ), Element.objects.get(name="Volume 1").id)
         self.assertCountEqual(results, expected)
diff --git a/arkindex/documents/tests/test_surface_importer.py b/arkindex/documents/tests/test_surface_importer.py
index 51d05bf5b1da86e85afc0a64a5822cdaa3bcf75a..79711f2d6a7e4426e727777bdabb0f9ce3346151 100644
--- a/arkindex/documents/tests/test_surface_importer.py
+++ b/arkindex/documents/tests/test_surface_importer.py
@@ -1,14 +1,11 @@
-from django.test import TestCase
+from arkindex.project.tests import FixtureTestCase
 from arkindex.documents.surface import parse_himanis_volume_name, parse_act_folio
-from arkindex.documents.models import Corpus, Element, ElementType, Page, PageType, PageDirection
+from arkindex.documents.models import Element, ElementType, Page, PageType, PageDirection
 
 
-class TestSurfaceImporterFunctions(TestCase):
+class TestSurfaceImporterFunctions(FixtureTestCase):
     """Test surface importing helper functions."""
 
-    def setUp(self):
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-
     def test_parse_himanis_volume_name(self):
         v42 = Element.objects.create(corpus=self.corpus, type=ElementType.Volume, name="Volume JJ 042")
         v43 = Element.objects.create(corpus=self.corpus, type=ElementType.Volume, name="Volume JJ043")
diff --git a/arkindex/documents/tests/test_surface_linker.py b/arkindex/documents/tests/test_surface_linker.py
index 6563ca118011354955889fcf912251977d74bb92..a47aa793f2b40457e4a4abf32be135c21a139ba7 100644
--- a/arkindex/documents/tests/test_surface_linker.py
+++ b/arkindex/documents/tests/test_surface_linker.py
@@ -1,212 +1,20 @@
 from unittest.mock import patch
-from django.test import TestCase
-from arkindex.project.polygon import Polygon
-from arkindex.documents.surface_link import parse_folios, ParsedFolio, SurfaceLinker
-from arkindex.documents.models import Corpus, Element, ElementType, \
-    Page, PageDirection, PageComplement, PageType
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.project.tests import FixtureTestCase
+from arkindex.documents.surface_link import ParsedFolio, SurfaceLinker
+from arkindex.documents.models import Element, ElementType
 
 
-class TestSurfaceLinkerFunctions(TestCase):
-    """Test SurfaceLinker helper functions."""
-
-    def setUp(self):
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        vol = Element.objects.create(corpus=self.corpus, type=ElementType.Volume, name="Volume")
-        self.p1r = Page.objects.create(corpus=self.corpus, name="1r", folio="1r", nb=1, direction=PageDirection.Recto)
-        self.p1v = Page.objects.create(corpus=self.corpus, name="1v", folio="1v", nb=1, direction=PageDirection.Verso)
-        self.p2r = Page.objects.create(corpus=self.corpus, name="2r", folio="2r", nb=2, direction=PageDirection.Recto)
-        self.p2bisr = Page.objects.create(
-            corpus=self.corpus,
-            name="2bisr",
-            folio="2bisr",
-            nb=2,
-            direction=PageDirection.Recto,
-            complement=PageComplement.Bis,
-        )
-        self.p2v = Page.objects.create(corpus=self.corpus, name="2v", folio="2v", nb=2, direction=PageDirection.Verso)
-        for page in (self.p1r, self.p1v, self.p2r, self.p2bisr, self.p2v):
-            page.add_parent(vol)
-        self.pages = list(Page.objects.get_descending(vol.id))
-
-    def test_page_order(self):
-        self.assertTrue(self.p1r < self.p1v)
-        self.assertTrue(self.p1v < self.p2r)
-        self.assertTrue(self.p1r < self.p2r)
-        self.assertTrue(self.p1r < self.p2v)
-        self.assertTrue(self.p2r < self.p2v)
-        self.assertTrue(self.p1r < self.p2bisr)
-        self.assertTrue(self.p1v < self.p2bisr)
-        self.assertTrue(self.p2r < self.p2bisr)
-        self.assertTrue(self.p2bisr < self.p2v)
-        self.assertTrue(self.p2v == self.p2v)
-
-    def test_parse_folios_no_check(self):
-        "Test parse_folios without a page queryset"
-
-        folios = parse_folios('14r')
-        self.assertEqual(len(folios), 1)
-        self.assertIsInstance(folios[0], ParsedFolio)
-        self.assertEqual(folios[0].number, 14)
-        self.assertEqual(folios[0].complement, None)
-        self.assertEqual(folios[0].direction, PageDirection.Recto)
-
-        folios = parse_folios('17bisv-019quat')
-        self.assertEqual(len(folios), 2)
-        self.assertIsInstance(folios[0], ParsedFolio)
-        self.assertEqual(folios[0].number, 17)
-        self.assertEqual(folios[0].complement, PageComplement.Bis)
-        self.assertEqual(folios[0].direction, PageDirection.Verso)
-        self.assertIsInstance(folios[1], ParsedFolio)
-        self.assertEqual(folios[1].number, 19)
-        self.assertEqual(folios[1].complement, PageComplement.Quat)
-        self.assertEqual(folios[1].direction, PageDirection.Recto)
-
-        with self.assertRaisesMessage(ValueError, 'Improperly formatted folio string'):
-            parse_folios('this is not a folio')
-
-    def test_parse_folios_ignore_check(self):
-        "Test parse_folios with a page queryset: ignore if there is only one folio"
-
-        folios = parse_folios('14r', pages=self.pages)
-        self.assertEqual(len(folios), 1)
-        self.assertIsInstance(folios[0], ParsedFolio)
-        self.assertEqual(folios[0].number, 14)
-        self.assertEqual(folios[0].complement, None)
-        self.assertEqual(folios[0].direction, PageDirection.Recto)
-
-    def test_parse_folios_check_success(self):
-        "Test parse_folios with a page queryset and a valid folio"
-
-        folios = parse_folios('2r-2v', pages=self.pages)
-        self.assertEqual(len(folios), 3)
-        self.assertIsInstance(folios[0], ParsedFolio)
-        self.assertEqual(folios[0].number, 2)
-        self.assertEqual(folios[0].complement, None)
-        self.assertEqual(folios[0].direction, PageDirection.Recto)
-        self.assertIsInstance(folios[1], ParsedFolio)
-        self.assertEqual(folios[1].number, 2)
-        self.assertEqual(folios[1].complement, PageComplement.Bis)
-        self.assertEqual(folios[1].direction, PageDirection.Recto)
-        self.assertIsInstance(folios[2], ParsedFolio)
-        self.assertEqual(folios[2].number, 2)
-        self.assertEqual(folios[2].complement, None)
-        self.assertEqual(folios[2].direction, PageDirection.Verso)
-
-    def test_parse_folios_wrong_order(self):
-        "Test parse_folios with a page queryset and a folio in a wrong order"
-
-        with self.assertRaisesMessage(ValueError, 'Folio seems to be in reverse order'):
-            parse_folios('1v-1r', pages=self.pages)
-
-
-class TestSurfaceLinker(TestCase):
+class TestSurfaceLinker(FixtureTestCase):
     """Test surface-act linking process."""
 
     def setUp(self):
-        # Create a server with three images
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img1 = Image.objects.create(path='img1', width=1337, height=42, server=self.imgsrv)
-        self.img2 = Image.objects.create(path='img2', width=255, height=420, server=self.imgsrv)
-        self.img3 = Image.objects.create(path='img3', width=418, height=404, server=self.imgsrv)
-
-        # Create zones for pages
-        self.z1 = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1337, 42), image=self.img1)
-        self.z2 = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 255, 420), image=self.img2)
-        self.z3 = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 418, 404), image=self.img3)
-
-        # Create zones for 6 surfaces, two per page
-        self.z1a = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1337, 22), image=self.img1)
-        self.z1b = Zone.objects.create(polygon=Polygon.from_coords(0, 22, 1337, 200), image=self.img1)
-        self.z2a = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 255, 210), image=self.img2)
-        self.z2b = Zone.objects.create(polygon=Polygon.from_coords(0, 210, 255, 420), image=self.img2)
-        self.z3a = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 418, 202), image=self.img3)
-        self.z3b = Zone.objects.create(polygon=Polygon.from_coords(0, 202, 1337, 42), image=self.img3)
-
-        # Create a volume and 3 pages
-        self.vol = Element.objects.create(corpus=self.corpus, name="Volume Name", type=ElementType.Volume)
-        self.p1 = Page.objects.create(
-            corpus=self.corpus,
-            name="p1",
-            folio="p1",
-            zone=self.z1,
-            nb=1,
-            direction=PageDirection.Verso,
-            page_type=PageType.Page,
-        )
-        self.p2 = Page.objects.create(
-            corpus=self.corpus,
-            name="p2",
-            folio="p2",
-            zone=self.z2,
-            nb=2,
-            complement=PageComplement.Ter,
-            direction=PageDirection.Recto,
-            page_type=PageType.Page,
-        )
-        self.p3 = Page.objects.create(
-            corpus=self.corpus,
-            name="p3",
-            folio="p3",
-            zone=self.z3,
-            nb=3,
-            direction=PageDirection.Verso,
-            page_type=PageType.Page,
-        )
-
-        # Create 6 surfaces
-        self.s1a = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P1 #1",
-            type=ElementType.Surface,
-            zone=self.z1a,
-        )
-        self.s1b = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P1 #2",
-            type=ElementType.Surface,
-            zone=self.z1b,
-        )
-        self.s2a = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P2 #1",
-            type=ElementType.Surface,
-            zone=self.z2a,
-        )
-        self.s2b = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P2 #2",
-            type=ElementType.Surface,
-            zone=self.z2b,
-        )
-        self.s3a = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P3 #1",
-            type=ElementType.Surface,
-            zone=self.z3a,
-        )
-        self.s3b = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface P3 #2",
-            type=ElementType.Surface,
-            zone=self.z3b,
-        )
-
-        # Link surfaces to pages
-        links = {
-            self.p1: [self.s1a, self.s1b],
-            self.p2: [self.s2a, self.s2b],
-            self.p3: [self.s3a, self.s3b],
-        }
-        for page, surfaces in links.items():
-            for surface in surfaces:
-                surface.add_parent(page)
-
-        # Link pages to vol
-        self.p1.add_parent(self.vol)
-        self.p2.add_parent(self.vol)
-        self.p3.add_parent(self.vol)
+        self.vol = Element.objects.get(name="Volume 1")
+        self.sa = Element.objects.get(name="Surface A")
+        self.sb = Element.objects.get(name="Surface B")
+        self.sc = Element.objects.get(name="Surface C")
+        self.sd = Element.objects.get(name="Surface D")
+        self.se = Element.objects.get(name="Surface E")
+        self.sf = Element.objects.get(name="Surface F")
 
     def test_init(self):
         "Test SurfaceLinker performs checks on its inputs"
@@ -240,15 +48,15 @@ class TestSurfaceLinker(TestCase):
         s.find_surfaces()
         self.assertEqual(s.surfaces_count, 6)
 
-        folio = ParsedFolio.from_raw('1', None, 'v')
+        folio = ParsedFolio.from_raw('1', None, 'r')
         surfaces = s._pop_surfaces(folio)
-        self.assertListEqual(surfaces, [self.s1a, self.s1b])
+        self.assertListEqual(surfaces, [self.sa, self.sb])
         self.assertEqual(len(s.surfaces[folio]), 0)
 
-        folio = ParsedFolio.from_raw('3', None, 'v')
+        folio = ParsedFolio.from_raw('2', None, 'r')
         surfaces = s._pop_surfaces(folio, index=0)
-        self.assertListEqual(surfaces, [self.s3a])
-        self.assertEqual(len(s.surfaces[folio]), 1)
+        self.assertListEqual(surfaces, [self.sd])
+        self.assertEqual(len(s.surfaces[folio]), 2)
 
     @patch('arkindex.documents.surface_link.logger')
     def test_pop_surfaces_warning(self, mock_logger):
@@ -284,8 +92,8 @@ class TestSurfaceLinker(TestCase):
         Test surface matching for an act with 2 folios on same page
         Greedy test case
         """
-        self.assertTrue(self.z1a.polygon.center.y < self.z1b.polygon.center.y)
-        s = SurfaceLinker(self.vol, [('1', '001v'), ('2', '001v')])
+        self.assertTrue(self.sa.zone.polygon.center.y < self.sb.zone.polygon.center.y)
+        s = SurfaceLinker(self.vol, [('1', '001r'), ('2', '001r')])
         s.find_pages()
         self.assertEqual(len(s.pages), 3)
         s.find_surfaces()
@@ -297,16 +105,16 @@ class TestSurfaceLinker(TestCase):
         self.assertEqual(s.parsed_acts, 2)
         self.assertEqual(s.linked_surfaces, 2)
         self.assertIn('1', s.acts_surfaces)
-        self.assertListEqual(s.acts_surfaces['1'], [self.s1a])
+        self.assertListEqual(s.acts_surfaces['1'], [self.sa])
         self.assertIn('2', s.acts_surfaces)
-        self.assertListEqual(s.acts_surfaces['2'], [self.s1b])
+        self.assertListEqual(s.acts_surfaces['2'], [self.sb])
 
     def test_two_folios(self):
         """
         Test surface matching for an act with two folios
         End of page 1 + start of page 2
         """
-        s = SurfaceLinker(self.vol, [('2', '1v-2terr')])
+        s = SurfaceLinker(self.vol, [('2', '1r-1v')])
         s.find_pages()
         self.assertEqual(len(s.pages), 3)
         s.find_surfaces()
@@ -318,14 +126,14 @@ class TestSurfaceLinker(TestCase):
         self.assertEqual(s.parsed_acts, 1)
         self.assertEqual(s.linked_surfaces, 2)
         self.assertIn('2', s.acts_surfaces)
-        self.assertListEqual(s.acts_surfaces['2'], [self.s1b, self.s2a])
+        self.assertListEqual(s.acts_surfaces['2'], [self.sb, self.sc])
 
     def test_three_folios(self):
         """
         Test surface matching for an act with three folios
         End of page 1 + full pages 2 + start of page 3
         """
-        s = SurfaceLinker(self.vol, [('3', '1v-3v')])
+        s = SurfaceLinker(self.vol, [('3', '1r-2r')])
         s.find_pages()
         self.assertEqual(len(s.pages), 3)
         s.find_surfaces()
@@ -336,9 +144,9 @@ class TestSurfaceLinker(TestCase):
         s.resolve_conflicts()
         s.link_surfaces()
         self.assertEqual(s.parsed_acts, 1)
-        self.assertEqual(s.linked_surfaces, 4)
+        self.assertEqual(s.linked_surfaces, 3)
         self.assertIn('3', s.acts_surfaces)
-        self.assertListEqual(s.acts_surfaces['3'], [self.s1b, self.s2a, self.s2b, self.s3a])
+        self.assertListEqual(s.acts_surfaces['3'], [self.sb, self.sc, self.sd])
 
     def test_wrong_folio(self):
         """
@@ -361,113 +169,9 @@ class TestSurfaceLinker(TestCase):
         """
         Test automatic detection of missing acts
         """
-        s = SurfaceLinker(self.vol, [('2', '1v'), ('4', '2terr')])
+        s = SurfaceLinker(self.vol, [('2', '1v'), ('4', '2r')])
         s.find_missing_acts()
-        self.assertListEqual(s.acts_raw, [('1', ''), ('2', '1v'), ('3', ''), ('4', '2terr')])
-
-
-class TestSurfaceLinkerConflict(TestCase):
-    """Test surface-act linking process and conflict solving."""
-
-    def setUp(self):
-        # Create a server with three images
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img1r = Image.objects.create(path='img1r', width=1000, height=1000, server=self.imgsrv)
-        self.img1v = Image.objects.create(path='img1v', width=1000, height=1000, server=self.imgsrv)
-        self.img2r = Image.objects.create(path='img2r', width=1000, height=1000, server=self.imgsrv)
-
-        # Create zones for pages
-        self.z1r = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 1000), image=self.img1r)
-        self.z1v = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 1000), image=self.img1v)
-        self.z2r = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 1000), image=self.img2r)
-
-        # Create zones for 5 surfaces
-        self.za = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 500), image=self.img1r)
-        self.zb = Zone.objects.create(polygon=Polygon.from_coords(0, 500, 1000, 1000), image=self.img1r)
-        self.zc = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 1000), image=self.img1v)
-        self.zd = Zone.objects.create(polygon=Polygon.from_coords(0, 0, 1000, 300), image=self.img2r)
-        self.ze = Zone.objects.create(polygon=Polygon.from_coords(0, 300, 1000, 600), image=self.img2r)
-        self.zf = Zone.objects.create(polygon=Polygon.from_coords(0, 600, 1000, 1000), image=self.img2r)
-
-        # Create a volume and 3 pages
-        self.vol = Element.objects.create(corpus=self.corpus, name="Volume Name", type=ElementType.Volume)
-        self.p1r = Page.objects.create(
-            corpus=self.corpus,
-            name="p1r",
-            folio="p1r",
-            zone=self.z1r,
-            nb=1,
-            direction=PageDirection.Recto,
-            page_type=PageType.Page,
-        )
-        self.p1v = Page.objects.create(
-            corpus=self.corpus,
-            name="p1v",
-            folio="p1v",
-            zone=self.z1v,
-            nb=1,
-            direction=PageDirection.Verso,
-            page_type=PageType.Page,
-        )
-        self.p2r = Page.objects.create(
-            corpus=self.corpus,
-            name="p2r",
-            folio="p2r",
-            zone=self.z2r,
-            nb=2,
-            direction=PageDirection.Recto,
-            page_type=PageType.Page,
-        )
-
-        # Create 6 surfaces
-        self.sa = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface A",
-            type=ElementType.Surface,
-            zone=self.za,
-        )
-        self.sb = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface B",
-            type=ElementType.Surface,
-            zone=self.zb,
-        )
-        self.sc = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface C",
-            type=ElementType.Surface,
-            zone=self.zc,
-        )
-        self.sd = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface D",
-            type=ElementType.Surface,
-            zone=self.zd,
-        )
-        self.se = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface E",
-            type=ElementType.Surface,
-            zone=self.ze,
-        )
-        self.sf = Element.objects.create(
-            corpus=self.corpus,
-            name="Surface F",
-            type=ElementType.Surface,
-            zone=self.zf,
-        )
-
-        # Link surfaces to pages
-        links = {
-            self.vol: [self.p1r, self.p1v, self.p2r],
-            self.p1r: [self.sa, self.sb],
-            self.p1v: [self.sc],
-            self.p2r: [self.sd, self.se, self.sf],
-        }
-        for parent, children in links.items():
-            for child in children:
-                child.add_parent(parent)
+        self.assertListEqual(s.acts_raw, [('1', ''), ('2', '1v'), ('3', ''), ('4', '2r')])
 
     def test_conflict(self):
         """
diff --git a/arkindex/documents/tests/test_surface_linker_functions.py b/arkindex/documents/tests/test_surface_linker_functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..8948406e21551dd32937c1c618b73bbeb084e5cb
--- /dev/null
+++ b/arkindex/documents/tests/test_surface_linker_functions.py
@@ -0,0 +1,95 @@
+from arkindex.project.tests import FixtureTestCase
+from arkindex.documents.models import Element, Page, PageDirection, PageComplement
+from arkindex.documents.surface_link import parse_folios, ParsedFolio
+
+
+class TestSurfaceLinkerFunctions(FixtureTestCase):
+    """Test SurfaceLinker helper functions."""
+
+    def setUp(self):
+        vol = Element.objects.get(name="Volume 1")
+        self.p1r = Page.objects.get(name="Volume 1, page 1r")
+        self.p1v = Page.objects.get(name="Volume 1, page 1v")
+        self.p2r = Page.objects.get(name="Volume 1, page 2r")
+        self.p2bisr = Page.objects.create(
+            corpus=self.corpus,
+            name="2bisr",
+            folio="2bisr",
+            nb=2,
+            direction=PageDirection.Recto,
+            complement=PageComplement.Bis,
+        )
+        self.p2v = Page.objects.create(corpus=self.corpus, name="2v", folio="2v", nb=2, direction=PageDirection.Verso)
+        for page in (self.p2bisr, self.p2v):
+            page.add_parent(vol)
+        self.pages = list(Page.objects.get_descending(vol.id))
+
+    def test_page_order(self):
+        self.assertTrue(self.p1r < self.p1v)
+        self.assertTrue(self.p1v < self.p2r)
+        self.assertTrue(self.p1r < self.p2r)
+        self.assertTrue(self.p1r < self.p2v)
+        self.assertTrue(self.p2r < self.p2v)
+        self.assertTrue(self.p1r < self.p2bisr)
+        self.assertTrue(self.p1v < self.p2bisr)
+        self.assertTrue(self.p2r < self.p2bisr)
+        self.assertTrue(self.p2bisr < self.p2v)
+        self.assertTrue(self.p2v == self.p2v)
+
+    def test_parse_folios_no_check(self):
+        "Test parse_folios without a page queryset"
+
+        folios = parse_folios('14r')
+        self.assertEqual(len(folios), 1)
+        self.assertIsInstance(folios[0], ParsedFolio)
+        self.assertEqual(folios[0].number, 14)
+        self.assertEqual(folios[0].complement, None)
+        self.assertEqual(folios[0].direction, PageDirection.Recto)
+
+        folios = parse_folios('17bisv-019quat')
+        self.assertEqual(len(folios), 2)
+        self.assertIsInstance(folios[0], ParsedFolio)
+        self.assertEqual(folios[0].number, 17)
+        self.assertEqual(folios[0].complement, PageComplement.Bis)
+        self.assertEqual(folios[0].direction, PageDirection.Verso)
+        self.assertIsInstance(folios[1], ParsedFolio)
+        self.assertEqual(folios[1].number, 19)
+        self.assertEqual(folios[1].complement, PageComplement.Quat)
+        self.assertEqual(folios[1].direction, PageDirection.Recto)
+
+        with self.assertRaisesMessage(ValueError, 'Improperly formatted folio string'):
+            parse_folios('this is not a folio')
+
+    def test_parse_folios_ignore_check(self):
+        "Test parse_folios with a page queryset: ignore if there is only one folio"
+
+        folios = parse_folios('14r', pages=self.pages)
+        self.assertEqual(len(folios), 1)
+        self.assertIsInstance(folios[0], ParsedFolio)
+        self.assertEqual(folios[0].number, 14)
+        self.assertEqual(folios[0].complement, None)
+        self.assertEqual(folios[0].direction, PageDirection.Recto)
+
+    def test_parse_folios_check_success(self):
+        "Test parse_folios with a page queryset and a valid folio"
+
+        folios = parse_folios('2r-2v', pages=self.pages)
+        self.assertEqual(len(folios), 3)
+        self.assertIsInstance(folios[0], ParsedFolio)
+        self.assertEqual(folios[0].number, 2)
+        self.assertEqual(folios[0].complement, None)
+        self.assertEqual(folios[0].direction, PageDirection.Recto)
+        self.assertIsInstance(folios[1], ParsedFolio)
+        self.assertEqual(folios[1].number, 2)
+        self.assertEqual(folios[1].complement, PageComplement.Bis)
+        self.assertEqual(folios[1].direction, PageDirection.Recto)
+        self.assertIsInstance(folios[2], ParsedFolio)
+        self.assertEqual(folios[2].number, 2)
+        self.assertEqual(folios[2].complement, None)
+        self.assertEqual(folios[2].direction, PageDirection.Verso)
+
+    def test_parse_folios_wrong_order(self):
+        "Test parse_folios with a page queryset and a folio in a wrong order"
+
+        with self.assertRaisesMessage(ValueError, 'Folio seems to be in reverse order'):
+            parse_folios('1v-1r', pages=self.pages)
diff --git a/arkindex/documents/tests/test_surface_parser.py b/arkindex/documents/tests/test_surface_parser.py
index 98c3a0ca2eec5c43c25489c9dd2551ee5feec5fa..8bcf8f6fcc7d6992598445c933673e679825f320 100644
--- a/arkindex/documents/tests/test_surface_parser.py
+++ b/arkindex/documents/tests/test_surface_parser.py
@@ -1,4 +1,4 @@
-from django.test import TestCase
+from unittest import TestCase
 from arkindex.documents.surface import SurfaceParser
 import os.path
 
diff --git a/arkindex/documents/tests/test_tasks.py b/arkindex/documents/tests/test_tasks.py
index 344320fc3291464500b5f01cd9d83aecdb8e9219..054f4b7d76937bd15aea560da6775cbac9e84e43 100644
--- a/arkindex/documents/tests/test_tasks.py
+++ b/arkindex/documents/tests/test_tasks.py
@@ -1,5 +1,5 @@
-from django.test import TestCase
 from fakeredis import FakeStrictRedis
+from unittest import TestCase
 from unittest.mock import patch
 from arkindex.documents.tasks import reindex_acts, reindex_transcriptions
 
diff --git a/arkindex/documents/tests/test_transcription_create.py b/arkindex/documents/tests/test_transcription_create.py
index 602fff86f0986e80d1e42995c5b88ad5a0b7b93b..b9b2c59d41b0abc7d4f73a3dfda7579dfb20eddf 100644
--- a/arkindex/documents/tests/test_transcription_create.py
+++ b/arkindex/documents/tests/test_transcription_create.py
@@ -1,39 +1,19 @@
 from django.urls import reverse
 from unittest.mock import patch
-from rest_framework.test import APITestCase
 from rest_framework import status
-from arkindex.documents.models import Corpus, Page, Transcription, ElementPath, ElementType
-from arkindex.images.models import ImageServer, Image, Zone
-from arkindex.users.models import User
+from arkindex.project.tests import FixtureAPITestCase
 from arkindex.project.polygon import Polygon
+from arkindex.documents.models import Page, Transcription, ElementPath, ElementType
 import uuid
 
 
-class TestTranscriptionCreate(APITestCase):
+class TestTranscriptionCreate(FixtureAPITestCase):
     """
     Tests for text element creation view
     """
 
     def setUp(self):
-        """
-        Create a page and an image with one already included transcription
-        """
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img = Image.objects.create(path='img', width=1337, height=1337, server=self.imgsrv)
-        pagezone = Zone.objects.create(polygon=[(0, 0), (1337, 0), (1337, 1337), (1337, 0), (0, 0)], image=self.img)
-        self.page = Page.objects.create(corpus=self.corpus, name="page", folio="page", zone=pagezone)
-        self.ts_zone = Zone.objects.create(
-            polygon=[(100, 200), (100, 300), (300, 300), (300, 200), (100, 200)], image=self.img)
-        self.ts = Transcription.objects.create(
-            corpus=self.corpus,
-            text="PAAMAYIM",
-            score=0.5,
-            zone=self.ts_zone,
-            type=ElementType.Word,
-        )
-        self.ts.add_parent(self.page)
-        self.user = User.objects.create_user(email='user@user.com', password='P45$w0rD')
+        self.page = Page.objects.get(corpus=self.corpus, zone__image__path='img1')
 
     def test_require_login(self):
         response = self.client.post(reverse('api:transcription-create'), format='json')
@@ -57,7 +37,7 @@ class TestTranscriptionCreate(APITestCase):
         self.assertEqual(new_ts.zone.polygon, Polygon.from_coords(0, 0, 100, 100))
         self.assertEqual(new_ts.score, 0.83)
         self.assertEqual(ElementPath.objects.filter(element_id=new_ts.id).count(), 1)
-        self.assertListEqual(ElementPath.objects.get(element=new_ts).path, [self.page.id])
+        self.assertIn(self.page.id, ElementPath.objects.get(element=new_ts).path)
         self.assertTrue(indexer.return_value.run_index.called)
 
     def test_check_element_type(self):
@@ -79,32 +59,36 @@ class TestTranscriptionCreate(APITestCase):
         Checks the view reuses zones when available
         """
         self.client.force_login(self.user)
+        ts = Transcription.objects.get(zone__image__path='img1', text="PARIS")
         response = self.client.post(reverse('api:transcription-create'), format='json', data={
             "type": "word",
             "element": str(self.page.id),
-            "polygon": self.ts_zone.polygon,
+            "polygon": ts.zone.polygon.serialize(),
             "text": "GLOUBIBOULGA",
             "score": 0.8,
         })
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
-        self.assertEqual(Transcription.objects.get(text="GLOUBIBOULGA").zone.id, self.ts_zone.id)
+        self.assertEqual(
+            Transcription.objects.get(text="GLOUBIBOULGA").zone.id, ts.zone.id)
 
     def test_update_transcription(self):
         """
         Checks the view updates transcriptions when they already exist
         """
         self.client.force_login(self.user)
+        ts = Transcription.objects.get(zone__image__path='img1', text="PARIS")
+        self.assertNotEqual(ts.score, 0.99)
         response = self.client.post(reverse('api:transcription-create'), format='json', data={
             "type": "word",
             "element": str(self.page.id),
-            "polygon": self.ts.zone.polygon,
-            "text": self.ts.text,
+            "polygon": ts.zone.polygon.serialize(),
+            "text": ts.text,
             "score": 0.99,
         })
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
-        self.assertEqual(Transcription.objects.filter(text=self.ts.text).count(), 1)
-        self.ts.refresh_from_db()
-        self.assertEqual(self.ts.score, 0.99)
+        self.assertEqual(Transcription.objects.filter(zone__image__path='img1', text=ts.text).count(), 1)
+        ts.refresh_from_db()
+        self.assertEqual(ts.score, 0.99)
 
     def test_invalid_data(self):
         """
@@ -151,7 +135,7 @@ class TestTranscriptionCreate(APITestCase):
         self.client.force_login(self.user)
         response = self.client.post(reverse('api:transcription-bulk'), format='json', data={
             "parent": str(self.page.id),
-            "image": str(self.img.id),
+            "image": str(self.page.zone.image.id),
             "transcriptions": [
                 {
                     "type": "word",
@@ -172,7 +156,7 @@ class TestTranscriptionCreate(APITestCase):
         self.assertEqual(new_ts.zone.polygon, Polygon.from_coords(0, 0, 100, 100))
         self.assertEqual(new_ts.score, 0.83)
         self.assertEqual(ElementPath.objects.filter(element_id=new_ts.id).count(), 1)
-        self.assertListEqual(ElementPath.objects.get(element=new_ts).path, [self.page.id])
+        self.assertIn(self.page.id, ElementPath.objects.get(element=new_ts).path)
 
         # indexer called
         self.assertTrue(indexer.return_value.run_index.called)
diff --git a/arkindex/documents/tests/test_volume_manifest.py b/arkindex/documents/tests/test_volume_manifest.py
index 603e6e4bd235b924174188a209265b74f9ec5bd1..b7fcfb3fe58719c54f113ce2e6474753d26c8ce9 100644
--- a/arkindex/documents/tests/test_volume_manifest.py
+++ b/arkindex/documents/tests/test_volume_manifest.py
@@ -1,31 +1,17 @@
 from django.urls import reverse
-from rest_framework.test import APITestCase
+from arkindex.project.tests import FixtureAPITestCase
 from rest_framework import status
 from tripoli import IIIFValidator
-from arkindex.documents.models import Corpus, Element, ElementType, Page
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.documents.models import Element, ElementType
 
 
-class TestVolumeManifestSerializer(APITestCase):
+class TestVolumeManifestSerializer(FixtureAPITestCase):
     """Tests for VolumeManifestSerializer class"""
 
-    def setUp(self):
-        # Create a volume with two pages and a server with two images
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img1 = Image.objects.create(path='img1', width=1337, height=42, server=self.imgsrv)
-        self.img2 = Image.objects.create(path='img2', width=255, height=420, server=self.imgsrv)
-        self.z1 = Zone.objects.create(polygon=[(0, 0), (1337, 0), (1337, 42), (42, 0), (0, 0)], image=self.img1)
-        self.z2 = Zone.objects.create(polygon=[(0, 0), (255, 0), (255, 420), (0, 420), (0, 0)], image=self.img2)
-        self.vol = Element.objects.create(corpus=self.corpus, name="Volume Name", type=ElementType.Volume)
-        self.p1 = Page.objects.create(corpus=self.corpus, name="p1", folio="p1", zone=self.z1)
-        self.p2 = Page.objects.create(corpus=self.corpus, name="p2", folio="p2", zone=self.z2)
-        self.p1.add_parent(self.vol)
-        self.p2.add_parent(self.vol)
-
     def test_normal_manifest(self):
         # Just a normal situation.
-        response = self.client.get(reverse('api:volume-manifest', kwargs={'pk': self.vol.id}))
+        vol = Element.objects.get(type=ElementType.Volume, name="Volume 1")
+        response = self.client.get(reverse('api:volume-manifest', kwargs={'pk': vol.id}))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         manifest = response.json()
 
@@ -40,25 +26,25 @@ class TestVolumeManifestSerializer(APITestCase):
         self.assertEqual(manifest['@type'], "sc:Manifest")
         self.assertEqual(manifest['@context'],
                          "http://iiif.io/api/presentation/2/context.json")
-        self.assertEqual(manifest['label'], self.vol.name)
+        self.assertEqual(manifest['label'], vol.name)
         self.assertEqual(manifest['thumbnail']['service']['@context'],
                          "http://iiif.io/api/image/2/context.json")
         self.assertEqual(manifest['thumbnail']['service']['profile'],
                          "http://iiif.io/api/image/2/level2.json")
         self.assertIn('server/img', manifest['thumbnail']['service']['@id'])
 
-        self.assertEqual(len(manifest['structures']), 3)
+        self.assertEqual(len(manifest['structures']), 4)
         topstruct = next(s for s in manifest['structures'] if s['viewingHint'] == "top")
         self.assertIsNotNone(topstruct)
-        self.assertEqual(topstruct['label'], self.vol.name)
+        self.assertEqual(topstruct['label'], vol.name)
         self.assertEqual(topstruct['@type'], 'sc:Range')
-        self.assertEqual(len(topstruct['ranges']), 2)
+        self.assertEqual(len(topstruct['ranges']), 3)
         self.assertEqual(manifest['@id'], topstruct['@id'])
 
         self.assertEqual(len(manifest['sequences']), 1)
         self.assertEqual(manifest['sequences'][0]['@type'], 'sc:Sequence')
         canvases = manifest['sequences'][0]['canvases']
-        self.assertEqual(len(canvases), 2)
+        self.assertEqual(len(canvases), 3)
         for canvas in canvases:
             self.assertIn('@type', canvas)
             self.assertIn('@id', canvas)
@@ -103,7 +89,8 @@ class TestVolumeManifestSerializer(APITestCase):
         self.assertEqual(len(manifest['sequences'][0]['canvases']), 0)
 
     def test_normal_manifest_iiif_validation(self):
-        response = self.client.get(reverse('api:volume-manifest', kwargs={'pk': self.vol.id}))
+        vol = Element.objects.get(type=ElementType.Volume, name="Volume 1")
+        response = self.client.get(reverse('api:volume-manifest', kwargs={'pk': vol.id}))
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         manifest = response.json()
         iv = IIIFValidator()
diff --git a/arkindex/images/tests.py b/arkindex/images/tests.py
index 3d66dc9ce8e57017f74f277928d8117f80b4e8fa..377508318d03d75010e20b2b42989dc88c40b44f 100644
--- a/arkindex/images/tests.py
+++ b/arkindex/images/tests.py
@@ -1,20 +1,18 @@
-from django.test import TestCase
-from arkindex.documents.models import Corpus, Page, Transcription, Element, ElementType
+from arkindex.project.tests import FixtureTestCase
 from arkindex.project.polygon import Polygon
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.documents.models import Transcription, Element, ElementType
+from arkindex.images.models import Image
 from arkindex.images.importer import bulk_transcriptions
 
 
-class TestBulkTranscriptions(TestCase):
+class TestBulkTranscriptions(FixtureTestCase):
     """Tests for bulk transcription and zone importing"""
 
     def setUp(self):
         # Create a page and an image
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
-        self.imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img = Image.objects.create(path='img', width=1337, height=42, server=self.imgsrv)
-        pagezone = Zone.objects.create(polygon=[(0, 0), (1337, 0), (1337, 42), (42, 0), (0, 0)], image=self.img)
-        self.page = Page.objects.create(corpus=self.corpus, name="page", folio="page", zone=pagezone)
+        self.img = Image.objects.get(path='img6', server=self.imgsrv)
+        pagezone = self.img.zones.first()
+        self.page = pagezone.elements.first()
 
     def test_bulk_transcriptions(self):
         items = [
@@ -38,7 +36,7 @@ class TestBulkTranscriptions(TestCase):
             },
         ]
         bulk_transcriptions(self.img, self.page, items)
-        out = Transcription.objects.all().order_by('line')
+        out = Transcription.objects.filter(zone__image=self.img).order_by('line')
 
         self.assertEqual(len(out), 2)
         self.assertIsInstance(out[0], Transcription)
@@ -112,7 +110,7 @@ class TestBulkTranscriptions(TestCase):
         out = bulk_transcriptions(self.img, self.page, items)
         self.assertEqual(len(out), 2)
 
-        trs = Transcription.objects.all().order_by('line')
+        trs = Transcription.objects.filter(zone__image=self.img).order_by('line')
 
         self.assertEqual(trs.count(), 2)
         self.assertIsInstance(trs[0], Transcription)
diff --git a/arkindex/project/polygon.py b/arkindex/project/polygon.py
index d19fa8b575dd574e84c7c3277405e34f99286d1a..9b126aa88809fa336d34302c049bc86497e9ec58 100644
--- a/arkindex/project/polygon.py
+++ b/arkindex/project/polygon.py
@@ -24,6 +24,8 @@ class Point(object):
         return '({0.x},{0.y})'.format(self)
 
     def __eq__(self, other):
+        if isinstance(other, collections.abc.Sequence):
+            return (self.x, self.y) == other
         return (isinstance(other, self.__class__) and
                 self.x == other.x and
                 self.y == other.y)
@@ -37,7 +39,7 @@ class Point(object):
                 self.y <= other.y)
 
 
-class Polygon(object):
+class Polygon(collections.abc.MutableSequence):
     '''
     A hashable Polygon in-memory
     '''
@@ -95,16 +97,35 @@ class Polygon(object):
         return "Polygon({})".format(', '.join(map(str, self.points)))
 
     def __eq__(self, other):
-        if len(self.points) != len(other.points):
+        other_points = other.points if hasattr(other, 'points') else other
+        if len(self.points) != len(other_points):
             return False
         return all(
             x == y
-            for x, y in zip(self.points, other.points)
+            for x, y in zip(self.points, other_points)
+        ) or all(
+            x == y
+            for x, y in zip(self.points, reversed(other_points))
         )
 
     def __hash__(self):
         return hash(tuple(self.points))
 
+    def __getitem__(self, i):
+        return self.points[i]
+
+    def __setitem__(self, i, value):
+        self.points[i] = value
+
+    def __delitem__(self, i):
+        del self.points[i]
+
+    def __len__(self):
+        return len(self.points)
+
+    def insert(self, i, value):
+        self.points.insert(i, value)
+
     def serialize(self):
         '''
         Used to save data in DB
diff --git a/arkindex/project/tests/__init__.py b/arkindex/project/tests/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ffc6bf62c5f02b8f520d5a97ef916887c3f5e984 100644
--- a/arkindex/project/tests/__init__.py
+++ b/arkindex/project/tests/__init__.py
@@ -0,0 +1,24 @@
+from django.test import TestCase
+from rest_framework.test import APITestCase
+from arkindex.documents.models import Corpus
+from arkindex.images.models import ImageServer
+from arkindex.users.models import User
+
+
+class FixtureMixin(object):
+    fixtures = ['data.json', ]
+
+    @classmethod
+    def setUpTestData(cls):
+        cls.corpus = Corpus.objects.get(id='test')
+        cls.user = User.objects.get(email='user@user.fr')
+        cls.superuser = User.objects.get(email='root@root.fr')
+        cls.imgsrv = ImageServer.objects.get(url='http://server')
+
+
+class FixtureTestCase(FixtureMixin, TestCase):
+    pass
+
+
+class FixtureAPITestCase(FixtureMixin, APITestCase):
+    pass
diff --git a/arkindex/project/tests/test_fields.py b/arkindex/project/tests/test_fields.py
index 80514df0a0de117f99fb7419bf1715097242de42..86cd0060e2967208a7e5ae733c5264497e694b0c 100644
--- a/arkindex/project/tests/test_fields.py
+++ b/arkindex/project/tests/test_fields.py
@@ -1,8 +1,8 @@
-from arkindex.documents.models import Element, ElementPath, Corpus, ElementType
-from django.test import TestCase
+from arkindex.documents.models import Element, ElementPath, ElementType
+from arkindex.project.tests import FixtureTestCase
 
 
-class TestArrayField(TestCase):
+class TestArrayField(FixtureTestCase):
     """
     Test the enhanced ArrayField
     """
@@ -11,7 +11,6 @@ class TestArrayField(TestCase):
         """
         D -> C -> B -> A
         """
-        self.corpus = Corpus.objects.create(id='test', name='Unit Tests')
         self.a, self.b, self.c, self.d = (
             Element.objects.create(corpus=self.corpus, name=str(i), type=ElementType.Volume)
             for i in range(4)
diff --git a/arkindex/project/tests/test_polygon.py b/arkindex/project/tests/test_polygon.py
index d26a8f7818420e99b401d57e1cd7e8b756bd2b8d..bd5d55dd42efe39f937cb1880a83da6899eb9350 100644
--- a/arkindex/project/tests/test_polygon.py
+++ b/arkindex/project/tests/test_polygon.py
@@ -1,4 +1,4 @@
-from django.test import TestCase
+from unittest import TestCase
 from arkindex.project.polygon import Polygon
 
 
diff --git a/arkindex/project/tests/test_polygonfield.py b/arkindex/project/tests/test_polygonfield.py
index b6282237e2cdd5b611a4c5549fe6f0591eebc9c4..e10ebae4c0550f32abe21f6f50e72b828ec4bff6 100644
--- a/arkindex/project/tests/test_polygonfield.py
+++ b/arkindex/project/tests/test_polygonfield.py
@@ -1,26 +1,31 @@
-from django.test import TestCase
-from arkindex.images.models import ImageServer, Image, Zone
+from arkindex.project.tests import FixtureTestCase
+from arkindex.images.models import Image, Zone
 
 
-class TestPolygonField(TestCase):
+class TestPolygonField(FixtureTestCase):
     '''
     Test the DB field & usage
     '''
 
     def setUp(self):
-        imgsrv = ImageServer.objects.create(name="Test Server", url="http://server")
-        self.img = Image.objects.create(path='img', width=1000, height=1000, server=imgsrv)
-        self.z1 = Zone.objects.create(
+        self.img = Image.objects.get(path='img3', server=self.imgsrv)
+        self.z1 = Zone.objects.get(
             image=self.img, polygon=[(0, 0), (0, 1000), (1000, 1000), (1000, 0), (0, 0)])
-        self.z2 = Zone.objects.create(
-            image=self.img, polygon=[(200, 200), (200, 300), (300, 300), (300, 200), (200, 200)])
-        self.z3 = Zone.objects.create(
-            image=self.img, polygon=[(500, 500), (500, 600), (600, 600), (600, 500), (500, 500)])
+        self.z2 = Zone.objects.get(
+            image=self.img, polygon=[(0, 0), (0, 300), (300, 300), (300, 0), (0, 0)])
+        self.z3 = Zone.objects.get(
+            image=self.img, polygon=[(300, 300), (300, 600), (600, 600), (600, 300), (300, 300)])
 
     def test_field(self):
-        self.assertListEqual(self.z1.polygon, [(0, 0), (0, 1000), (1000, 1000), (1000, 0), (0, 0)])
+        self.assertSequenceEqual(self.z1.polygon, [(0, 0), (0, 1000), (1000, 1000), (1000, 0), (0, 0)])
 
     def test_contains(self):
-        self.assertSequenceEqual(Zone.objects.filter(polygon__contains=self.z2.polygon), [self.z1, self.z2])
-        self.assertSequenceEqual(Zone.objects.filter(polygon__contains=self.z3.polygon), [self.z1, self.z3])
-        self.assertSequenceEqual(Zone.objects.filter(polygon__contains=self.z1.polygon), [self.z1])
+        self.assertCountEqual(
+            Zone.objects.filter(image=self.img, polygon__contains=self.z2.polygon),
+            [self.z1, self.z2])
+        self.assertCountEqual(
+            Zone.objects.filter(image=self.img, polygon__contains=self.z3.polygon),
+            [self.z1, self.z3])
+        self.assertCountEqual(
+            Zone.objects.filter(image=self.img, polygon__contains=self.z1.polygon),
+            [self.z1])
diff --git a/arkindex/project/tests/test_tools.py b/arkindex/project/tests/test_tools.py
index ea245978e2af2313ac3110814e3f1448cd303683..b9c1cffd7612ff581b44755d1c5af62a4a0c8e57 100644
--- a/arkindex/project/tests/test_tools.py
+++ b/arkindex/project/tests/test_tools.py
@@ -1,4 +1,4 @@
-from django.test import TestCase
+from unittest import TestCase
 from arkindex.project.tools import elasticsearch_escape
 
 
diff --git a/requirements.txt b/requirements.txt
index 0cfe25a62188e46aabc6d34070464967869fd135..f723301e560f4813a50734a637e68f8ac3686fce 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@ celery_once==2.0.0
 certifi==2017.7.27.1
 chardet==3.0.4
 Django==2.0
-django-enumfields==0.9.0
+django-enumfields==0.10.0
 djangorestframework==3.7.1
 django-webpack-loader==0.5.0
 elasticsearch==6.2.0