girder 5.0.19.dev60__tar.gz → 5.0.19.dev76__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/config.yml +27 -7
  2. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.pre-commit-config.yaml +1 -0
  3. {girder-5.0.19.dev60/girder.egg-info → girder-5.0.19.dev76}/PKG-INFO +1 -1
  4. girder-5.0.19.dev76/girder/utility/_hash_state.py +261 -0
  5. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/playwright-teardown.ts +2 -2
  6. girder-5.0.19.dev76/girder/web/tests/spec/plugin_autojoin.spec.ts +100 -0
  7. girder-5.0.19.dev76/girder/web/tests/spec/plugin_hashsum_download.spec.ts +74 -0
  8. girder-5.0.19.dev76/girder/web/tests/spec/plugin_homepage.spec.ts +118 -0
  9. girder-5.0.19.dev76/girder/web/tests/spec/plugin_item_licenses.spec.ts +257 -0
  10. girder-5.0.19.dev76/girder/web/tests/spec/plugin_jobs.spec.ts +562 -0
  11. girder-5.0.19.dev76/girder/web/tests/spec/plugin_readme.spec.ts +52 -0
  12. girder-5.0.19.dev76/girder/web/tests/spec/plugin_terms.spec.ts +263 -0
  13. girder-5.0.19.dev76/girder/web/tests/spec/plugin_user_quota.spec.ts +434 -0
  14. girder-5.0.19.dev76/girder/web/tests/spec/utilities.spec.ts +434 -0
  15. girder-5.0.19.dev76/girder/web/tests/spec/version.spec.ts +60 -0
  16. girder-5.0.19.dev76/girder/web/tests/spec/widgets.spec.ts +290 -0
  17. {girder-5.0.19.dev60 → girder-5.0.19.dev76/girder.egg-info}/PKG-INFO +1 -1
  18. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/SOURCES.txt +5 -0
  19. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/scm_file_list.json +5 -0
  20. girder-5.0.19.dev76/girder.egg-info/scm_version.json +8 -0
  21. girder-5.0.19.dev60/girder/utility/_hash_state.py +0 -152
  22. girder-5.0.19.dev60/girder/web/tests/spec/plugin_hashsum_download.spec.ts +0 -26
  23. girder-5.0.19.dev60/girder/web/tests/spec/plugin_homepage.spec.ts +0 -37
  24. girder-5.0.19.dev60/girder/web/tests/spec/plugin_item_licenses.spec.ts +0 -26
  25. girder-5.0.19.dev60/girder/web/tests/spec/plugin_readme.spec.ts +0 -24
  26. girder-5.0.19.dev60/girder/web/tests/spec/plugin_terms.spec.ts +0 -38
  27. girder-5.0.19.dev60/girder/web/tests/spec/plugin_user_quota.spec.ts +0 -39
  28. girder-5.0.19.dev60/girder.egg-info/scm_version.json +0 -8
  29. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/Dockerfile +0 -0
  30. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/build_plugins.py +0 -0
  31. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/generatePyEnvChecksum.sh +0 -0
  32. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/publish_npm.sh +0 -0
  33. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/publish_pypi.sh +0 -0
  34. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.circleci/rabbitmq.conf +0 -0
  35. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.codecov.yml +0 -0
  36. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.dockerignore +0 -0
  37. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.editorconfig +0 -0
  38. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.git-blame-ignore-revs +0 -0
  39. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.github/workflows/codespell.yml +0 -0
  40. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.gitignore +0 -0
  41. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/.readthedocs.yml +0 -0
  42. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/CHANGELOG.rst +0 -0
  43. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/CMakeLists.txt +0 -0
  44. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/CONTRIBUTING.rst +0 -0
  45. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/Dockerfile +0 -0
  46. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/LICENSE +0 -0
  47. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/MANIFEST.in +0 -0
  48. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/README.rst +0 -0
  49. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/README.rst +0 -0
  50. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/girder_client/__init__.py +0 -0
  51. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/girder_client/_jsonemitter.py +0 -0
  52. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/girder_client/cli.py +0 -0
  53. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/pyproject.toml +0 -0
  54. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/clients/python/setup.py +0 -0
  55. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docker-compose.yml +0 -0
  56. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/admin-docs.rst +0 -0
  57. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/api-docs.rst +0 -0
  58. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/build-docs.rst +0 -0
  59. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/conf.py +0 -0
  60. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/configuration.rst +0 -0
  61. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/dependencies.rst +0 -0
  62. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/deployment.rst +0 -0
  63. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/dev-installation.rst +0 -0
  64. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/developer-cookbook.rst +0 -0
  65. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/developer-docs.rst +0 -0
  66. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/development.rst +0 -0
  67. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/favicon.ico +0 -0
  68. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/images/dicom-viewer.png +0 -0
  69. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/index.rst +0 -0
  70. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/license.png +0 -0
  71. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/list_env_settings.py +0 -0
  72. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/list_env_settings.yaml +0 -0
  73. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/migration-guide.rst +0 -0
  74. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/mount.rst +0 -0
  75. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/plugin-development.rst +0 -0
  76. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/plugins.rst +0 -0
  77. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/python-client.rst +0 -0
  78. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/requirements-docs.txt +0 -0
  79. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/security.rst +0 -0
  80. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/sftp.rst +0 -0
  81. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/static/custom.css +0 -0
  82. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/user-docs.rst +0 -0
  83. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/docs/user-guide.rst +0 -0
  84. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/__init__.py +0 -0
  85. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/__init__.py +0 -0
  86. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/access.py +0 -0
  87. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/api_docs.mako +0 -0
  88. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/api_main.py +0 -0
  89. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/describe.py +0 -0
  90. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/docs.py +0 -0
  91. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/filter_logging.py +0 -0
  92. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/rest.py +0 -0
  93. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/sftp.py +0 -0
  94. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/__init__.py +0 -0
  95. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/api_key.py +0 -0
  96. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/assetstore.py +0 -0
  97. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/collection.py +0 -0
  98. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/file.py +0 -0
  99. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/folder.py +0 -0
  100. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/group.py +0 -0
  101. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/item.py +0 -0
  102. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/resource.py +0 -0
  103. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/system.py +0 -0
  104. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/token.py +0 -0
  105. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/api/v1/user.py +0 -0
  106. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/asgi.py +0 -0
  107. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/cli/__init__.py +0 -0
  108. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/cli/mount.py +0 -0
  109. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/cli/serve.py +0 -0
  110. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/cli/sftpd.py +0 -0
  111. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/cli/shell.py +0 -0
  112. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/constants.py +0 -0
  113. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/events.py +0 -0
  114. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/exceptions.py +0 -0
  115. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/_footer.mako +0 -0
  116. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/_header.mako +0 -0
  117. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/accountApproval.mako +0 -0
  118. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/accountApproved.mako +0 -0
  119. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/emailVerification.mako +0 -0
  120. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/groupInvite.mako +0 -0
  121. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/mail_templates/temporaryAccess.mako +0 -0
  122. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/__init__.py +0 -0
  123. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/api_key.py +0 -0
  124. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/assetstore.py +0 -0
  125. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/collection.py +0 -0
  126. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/file.py +0 -0
  127. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/folder.py +0 -0
  128. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/group.py +0 -0
  129. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/item.py +0 -0
  130. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/model_base.py +0 -0
  131. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/setting.py +0 -0
  132. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/token.py +0 -0
  133. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/upload.py +0 -0
  134. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/models/user.py +0 -0
  135. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/notification.py +0 -0
  136. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/plugin.py +0 -0
  137. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/settings.py +0 -0
  138. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/tasks.py +0 -0
  139. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/__init__.py +0 -0
  140. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/_cache.py +0 -0
  141. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/abstract_assetstore_adapter.py +0 -0
  142. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/acl_mixin.py +0 -0
  143. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/assetstore_utilities.py +0 -0
  144. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/config.py +0 -0
  145. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/error.mako +0 -0
  146. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/filesystem_assetstore_adapter.py +0 -0
  147. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/mail_utils.py +0 -0
  148. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/model_importer.py +0 -0
  149. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/path.py +0 -0
  150. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/progress.py +0 -0
  151. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/s3_assetstore_adapter.py +0 -0
  152. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/search.py +0 -0
  153. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/server.py +0 -0
  154. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/setting_utilities.py +0 -0
  155. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/system.py +0 -0
  156. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/webroot.py +0 -0
  157. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/utility/ziputil.py +0 -0
  158. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/.env.development +0 -0
  159. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/.gitignore +0 -0
  160. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/.prettierrc.json +0 -0
  161. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/README.rst +0 -0
  162. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/Girder_Favicon.png +0 -0
  163. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/Girder_Mark.png +0 -0
  164. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/fontello-BGLM866c.svg +0 -0
  165. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/fontello-CMxI46LA.eot +0 -0
  166. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/fontello-Ddaw8Z2O.woff2 +0 -0
  167. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/fontello-KuItqCW9.ttf +0 -0
  168. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/fontello-_lT35XxX.woff +0 -0
  169. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/glyphicons-halflings-regular-BKjkU69z.woff +0 -0
  170. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/glyphicons-halflings-regular-BUJKDMgK.eot +0 -0
  171. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/glyphicons-halflings-regular-CSehLiBc.svg +0 -0
  172. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/glyphicons-halflings-regular-DrwTMapi.ttf +0 -0
  173. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/glyphicons-halflings-regular-DzqM6ju8.woff2 +0 -0
  174. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/index-C3KtNRrl.js +0 -0
  175. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/index-C3KtNRrl.js.map +0 -0
  176. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/index-DFhDyJFH.css +0 -0
  177. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/jsoneditor-icons-CIlYC0TY.svg +0 -0
  178. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-300-B6tDWRxp.woff2 +0 -0
  179. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-300-DZbNTRe6.woff +0 -0
  180. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-300italic-DLsXqxhU.woff2 +0 -0
  181. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-300italic-Dfz31Up5.woff +0 -0
  182. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-400-D5zbb_EN.woff +0 -0
  183. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-400-DLgeRt71.woff2 +0 -0
  184. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-400italic-DpGQ6Ktj.woff2 +0 -0
  185. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-400italic-UJbezL8h.woff +0 -0
  186. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-600-CCvT4JHQ.woff2 +0 -0
  187. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-600-DHWfDY-G.woff +0 -0
  188. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-600italic-X8kCjSq_.woff +0 -0
  189. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-600italic-slqXzGub.woff2 +0 -0
  190. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-700-CsbQbrjx.woff2 +0 -0
  191. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-700-DNl7WOH5.woff +0 -0
  192. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-700italic-BtE7VvhE.woff2 +0 -0
  193. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-700italic-hc2P_kZo.woff +0 -0
  194. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-800-CbJOM4_m.woff2 +0 -0
  195. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-800-XaYTAPrv.woff +0 -0
  196. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-800italic-DqvYew-K.woff +0 -0
  197. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/assets/open-sans-latin-800italic-JIGkTxpX.woff2 +0 -0
  198. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/index.html +0 -0
  199. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/auth.js +0 -0
  200. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/constants.js +0 -0
  201. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/dialog.js +0 -0
  202. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/events.js +0 -0
  203. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/index.ts +0 -0
  204. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/main.ts +0 -0
  205. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/misc.js +0 -0
  206. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/pluginUtils.js +0 -0
  207. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/rest.js +0 -0
  208. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/router.js +0 -0
  209. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/routes.js +0 -0
  210. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/version.js +0 -0
  211. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/src/src/vite-env.d.ts +0 -0
  212. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/swagger-ui/swagger-ui-bundle.js +0 -0
  213. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/dist/swagger-ui/swagger-ui.css +0 -0
  214. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/env.d.ts +0 -0
  215. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/playwright.config.ts +0 -0
  216. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/coverage.ts +0 -0
  217. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/playwright-setup.ts +0 -0
  218. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/server.ts +0 -0
  219. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/admin.spec.ts +0 -0
  220. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/browser.spec.ts +0 -0
  221. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/collection.spec.ts +0 -0
  222. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/collectionBaseClass.spec.ts +0 -0
  223. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/customWidgets.spec.ts +0 -0
  224. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/README.md +0 -0
  225. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/fake.jpg +0 -0
  226. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/nomime.dat +0 -0
  227. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/slicer_cli_web_multiple_test.xml +0 -0
  228. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/slicer_cli_web_test.xml +0 -0
  229. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/ten_byte_file.txt +0 -0
  230. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/testFile.txt +0 -0
  231. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data/testFile2 +0 -0
  232. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/data.spec.ts +0 -0
  233. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/emptyLayout.spec.ts +0 -0
  234. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/file_upload.spec.ts +0 -0
  235. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/folder.spec.ts +0 -0
  236. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/group.spec.ts +0 -0
  237. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/item.spec.ts +0 -0
  238. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/model.spec.ts +0 -0
  239. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_authorized_upload.spec.ts +0 -0
  240. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web.spec.ts +0 -0
  241. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_config_view.spec.ts +0 -0
  242. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_container_view.spec.ts +0 -0
  243. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_item_selector.spec.ts +0 -0
  244. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_panel_group.spec.ts +0 -0
  245. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_parser.spec.ts +0 -0
  246. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_task.spec.ts +0 -0
  247. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_widget_model.spec.ts +0 -0
  248. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_slicer_cli_web_widget_view.spec.ts +0 -0
  249. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/plugin_thumbnails.spec.ts +0 -0
  250. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/routing.spec.ts +0 -0
  251. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/setApi.spec.ts +0 -0
  252. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/setting_show_download.spec.ts +0 -0
  253. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/sortWidget.spec.ts +0 -0
  254. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/swagger.spec.ts +0 -0
  255. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/spec/user.spec.ts +0 -0
  256. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/web/tests/util.ts +0 -0
  257. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/worker_plugin.py +0 -0
  258. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder/wsgi.py +0 -0
  259. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/dependency_links.txt +0 -0
  260. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/entry_points.txt +0 -0
  261. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/not-zip-safe +0 -0
  262. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/requires.txt +0 -0
  263. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/girder.egg-info/top_level.txt +0 -0
  264. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/jsdoc.json +0 -0
  265. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/package-lock.json +0 -0
  266. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/package.json +0 -0
  267. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pyproject.toml +0 -0
  268. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/LICENSE +0 -0
  269. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/MANIFEST.in +0 -0
  270. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pyproject.toml +0 -0
  271. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/__init__.py +0 -0
  272. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/assertions.py +0 -0
  273. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/fixtures.py +0 -0
  274. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/plugin.py +0 -0
  275. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/plugin_registry.py +0 -0
  276. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/utils.py +0 -0
  277. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/pytest_girder/web_client.py +0 -0
  278. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/pytest_girder/setup.py +0 -0
  279. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/requirements-dev.txt +0 -0
  280. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/midas/README.rst +0 -0
  281. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/midas/migrate.py +0 -0
  282. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/midas/requirements.txt +0 -0
  283. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/midas/walk_girder.py +0 -0
  284. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/midas/walk_midas.py +0 -0
  285. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/publicNames.py +0 -0
  286. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/publicNames.txt +0 -0
  287. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/scripts/test_names.sh +0 -0
  288. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/setup.cfg +0 -0
  289. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/setup.py +0 -0
  290. {girder-5.0.19.dev60 → girder-5.0.19.dev76}/tox.ini +0 -0
@@ -175,6 +175,11 @@ jobs:
175
175
  web-test:
176
176
  executor: py3-mongo
177
177
  resource_class: large
178
+ parameters:
179
+ browser:
180
+ description: "Playwright project / browser to run the web tests against"
181
+ type: enum
182
+ enum: [chromium, firefox]
178
183
  steps:
179
184
  - checkout:
180
185
  path: girder
@@ -188,20 +193,26 @@ jobs:
188
193
  - build-plugins
189
194
  - run:
190
195
  name: Install playwright browser dependencies
191
- command: npm run install-browsers
196
+ command: npx playwright install --with-deps << parameters.browser >>
192
197
  working_directory: girder/girder/web
193
198
  - run:
194
- name: Test @girder/core
195
- command: npm run test
199
+ name: Test @girder/core (<< parameters.browser >>)
200
+ command: npm run test -- --project=<< parameters.browser >>
196
201
  working_directory: girder/girder/web
197
202
  environment:
198
203
  PLAYWRIGHT_WORKERS: 4
204
+ GIRDER_TEST_COVERAGE_DIR: ../../build/test/coverage/<< parameters.browser >>
205
+ GIRDER_TEST_COVERAGE_FILE: cobertura-coverage.xml
199
206
  - store_artifacts:
200
207
  path: girder/girder/web/coverage/report
201
- destination: coverage
208
+ destination: coverage-<< parameters.browser >>
202
209
  - store_artifacts:
203
210
  path: girder/girder/web/test-results
204
- destination: test-results
211
+ destination: test-results-<< parameters.browser >>
212
+ - persist_to_workspace:
213
+ root: /home/circleci/project
214
+ paths:
215
+ - girder/build/test/coverage/<< parameters.browser >>
205
216
 
206
217
  integration-test:
207
218
  executor: machine
@@ -316,7 +327,7 @@ jobs:
316
327
  working_directory: girder
317
328
  - run:
318
329
  name: Upload coverage
319
- command: ./codecov --disable search pycov gcov --root girder --required --file build/test/coverage/py_coverage.xml build/test/coverage/cobertura-coverage.xml
330
+ command: ./codecov --disable search pycov gcov --root girder --required --file build/test/coverage/py_coverage.xml build/test/coverage/chromium/cobertura-coverage.xml build/test/coverage/firefox/cobertura-coverage.xml
320
331
  working_directory: girder
321
332
 
322
333
  dockers:
@@ -424,6 +435,14 @@ workflows:
424
435
  tags:
425
436
  only: /^v[0-9]+.*/
426
437
  - web-test:
438
+ name: web-test-chromium
439
+ browser: chromium
440
+ filters:
441
+ tags:
442
+ only: /^v[0-9]+.*/
443
+ - web-test:
444
+ name: web-test-firefox
445
+ browser: firefox
427
446
  filters:
428
447
  tags:
429
448
  only: /^v[0-9]+.*/
@@ -435,7 +454,8 @@ workflows:
435
454
  requires:
436
455
  - server-pytest-test
437
456
  - server-legacy-test
438
- - web-test
457
+ - web-test-chromium
458
+ - web-test-firefox
439
459
  - integration-test
440
460
  filters:
441
461
  tags:
@@ -62,6 +62,7 @@ repos:
62
62
  rev: v0.0.18
63
63
  hooks:
64
64
  - id: circleci-config-validate
65
+ files: ^\.circleci/.*\.yml$
65
66
  - repo: https://github.com/asottile/pyupgrade
66
67
  rev: v3.21.2
67
68
  hooks:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder
3
- Version: 5.0.19.dev60
3
+ Version: 5.0.19.dev76
4
4
  Summary: Web-based data management platform
5
5
  Home-page: https://girder.readthedocs.org
6
6
  Author: Kitware, Inc.
@@ -0,0 +1,261 @@
1
+ # Utility for saving and restoring the internal state of a hash object
2
+ # so that checksums can be streamed without having to remain in memory.
3
+ # Inspired by http://code.activestate.com/recipes/
4
+ # 578479-save-and-restore-sha-512-internal-state/
5
+
6
+ import binascii
7
+ import ctypes
8
+ import hashlib
9
+ import ssl
10
+ import sys
11
+
12
+ _ver = sys.version_info
13
+ _free_threaded = hasattr(sys, '_is_gil_enabled') and not sys._is_gil_enabled()
14
+ _WORD = ctypes.sizeof(ctypes.c_void_p)
15
+
16
+ # This is the offset to the EVP_MD_CTX structure in CPython's _hashopenssl.c
17
+ # EVPObject. It changed for Python 3.8 in
18
+ # https://github.com/python/cpython/pull/16023 and again for the free-threaded
19
+ # builds in 3.14 (which add two words to the object header) and for all builds
20
+ # in 3.15 (which add one word). These values are only used as a fallback on
21
+ # platforms where we cannot safely auto-detect the layout.
22
+ if _free_threaded:
23
+ _HASHLIB_EVP_STRUCT_OFFSET = 5 if _ver >= (3, 15) else 4
24
+ else:
25
+ _HASHLIB_EVP_STRUCT_OFFSET = 3 if _ver >= (3, 15) else 2
26
+
27
+ # Number of pointer-sized words to scan when auto-detecting the layout of
28
+ # CPython's _hashlib.HASH object and the OpenSSL EVP_MD_CTX structure it
29
+ # references. The digest state has never been stored inline in the hash
30
+ # object (since CPython 2.7.13), so it is always reachable via two levels of
31
+ # pointers: the hash object points to an EVP_MD_CTX, which points to the
32
+ # state data.
33
+ _HASHLIB_OBJECT_SCAN_WORDS = 10
34
+ _EVP_MD_CTX_SCAN_WORDS = 16
35
+
36
+ # Minimum plausible heap address; anything smaller is not a real pointer.
37
+ _MIN_VALID_ADDRESS = 0x1000
38
+
39
+ # Auto-detected offsets, in units of words: (offset of the EVP_MD_CTX pointer
40
+ # within the hash object, offset of the state data pointer within the
41
+ # EVP_MD_CTX). Set once the first hash object has been inspected and reused
42
+ # for all subsequent objects, since the layout is the same for every hash
43
+ # algorithm within a single interpreter build.
44
+ _STATE_OFFSETS = None
45
+
46
+
47
+ def _readPointer(address):
48
+ return ctypes.cast(address, ctypes.POINTER(ctypes.c_void_p))[0]
49
+
50
+
51
+ def _getReadableAddressRanges():
52
+ """
53
+ Get the readable memory ranges of the current process, used to validate
54
+ candidate pointers before dereferencing them.
55
+
56
+ :returns: A list of (start, end) address tuples, or None if the platform
57
+ does not provide the information (in which case hardcoded offsets are
58
+ used instead).
59
+ """
60
+ try:
61
+ with open('/proc/self/maps') as mapsFile:
62
+ ranges = []
63
+ for line in mapsFile:
64
+ fields = line.split()
65
+ if 'r' not in fields[1]:
66
+ continue
67
+ start, end = fields[0].split('-')
68
+ ranges.append((int(start, 16), int(end, 16)))
69
+ return ranges
70
+ except (OSError, IndexError, ValueError):
71
+ return None
72
+
73
+
74
+ def _isValidAddress(ranges, address, size):
75
+ """
76
+ Check that an address range is safe to read without segfaulting.
77
+ """
78
+ if not address or address < _MIN_VALID_ADDRESS:
79
+ return False
80
+ return any(start <= address and address + size <= end for start, end in ranges)
81
+
82
+
83
+ def _locateStateDataAddress(hashObject, initVectorFirstWord, stateSize):
84
+ """
85
+ Find the address of the internal hash state of a hash object.
86
+
87
+ When possible, this is done by scanning the hash object for a pointer to
88
+ an OpenSSL EVP_MD_CTX and scanning that for a pointer to memory that
89
+ begins with the algorithm's initialization vector. This avoids depending
90
+ on hardcoded struct offsets, which CPython has repeatedly changed (see
91
+ the fallback offsets below the imports). On platforms where memory
92
+ ranges cannot be enumerated, the hardcoded offsets are used instead.
93
+
94
+ :param hashObject: A hashlib hash object backed by OpenSSL
95
+ (_hashlib.HASH).
96
+ :param initVectorFirstWord: The first eight bytes of the digest state of a
97
+ freshly initialized hash object of this algorithm.
98
+ :param stateSize: The size in bytes of the digest state.
99
+ :return: The address of the digest state, or None if it could not be
100
+ located.
101
+ """
102
+ global _STATE_OFFSETS
103
+ objectAddress = id(hashObject)
104
+
105
+ if _STATE_OFFSETS is not None:
106
+ objectOffset, ctxOffset = _STATE_OFFSETS
107
+ ctxAddress = _readPointer(objectAddress + objectOffset * _WORD)
108
+ return _readPointer(ctxAddress + ctxOffset * _WORD)
109
+
110
+ ranges = _getReadableAddressRanges()
111
+ if ranges is None:
112
+ # Platforms without /proc/self/maps, such as macOS and Windows.
113
+ ctxAddress = _readPointer(objectAddress + _HASHLIB_EVP_STRUCT_OFFSET * _WORD)
114
+ if ssl._OPENSSL_API_VERSION < (3, 0):
115
+ # OpenSSL 1.x
116
+ return _readPointer(ctxAddress + 3 * _WORD)
117
+ # OpenSSL 3.x
118
+ return _readPointer(ctxAddress + 7 * _WORD)
119
+
120
+ for objectOffset in range(2, _HASHLIB_OBJECT_SCAN_WORDS):
121
+ ctxAddress = _readPointer(objectAddress + objectOffset * _WORD)
122
+ if not _isValidAddress(ranges, ctxAddress, _EVP_MD_CTX_SCAN_WORDS * _WORD):
123
+ continue
124
+ for ctxOffset in range(_EVP_MD_CTX_SCAN_WORDS):
125
+ stateAddress = _readPointer(ctxAddress + ctxOffset * _WORD)
126
+ if not _isValidAddress(ranges, stateAddress, stateSize):
127
+ continue
128
+ if ctypes.cast(
129
+ stateAddress, ctypes.POINTER(ctypes.c_char)
130
+ )[:8] == initVectorFirstWord:
131
+ _STATE_OFFSETS = (objectOffset, ctxOffset)
132
+ return stateAddress
133
+ return None
134
+
135
+
136
+ def _getStateDataPointer(hashObject, hashInfo):
137
+ """
138
+ Get a pointer to the internal hash state of a hash object.
139
+
140
+ :param hashObject: A hashlib hash object.
141
+ :param hashInfo: The _HashInfo instance describing the hash algorithm.
142
+ :return: A ctypes pointer to the internal hash state.
143
+ :rtype: ctypes.POINTER(ctypes.c_char)
144
+ """
145
+ stateAddress = _locateStateDataAddress(
146
+ hashObject, hashInfo.initVectorFirstWord, hashInfo.stateSize)
147
+ if stateAddress is None:
148
+ raise RuntimeError(
149
+ 'Could not locate the internal state of the %s hash object. This module '
150
+ 'requires a CPython build with OpenSSL-backed hashlib.' % hashInfo.name)
151
+ return ctypes.cast(stateAddress, ctypes.POINTER(ctypes.c_char))
152
+
153
+
154
+ _HASH_INFOS = {}
155
+
156
+
157
+ class _HashInfo:
158
+ def __init__(self, type, stateSize, initVectorFirstWord):
159
+ self.type = type
160
+ self.stateSize = stateSize
161
+ self.initVectorFirstWord = initVectorFirstWord
162
+
163
+ # Instantiate to get the name and do a sanity check
164
+ instance = self.type()
165
+ self.name = instance.name
166
+
167
+ stateDataPointer = _getStateDataPointer(instance, self)
168
+ assert stateDataPointer[:8] == initVectorFirstWord
169
+
170
+ _HASH_INFOS[self.name] = self
171
+
172
+
173
+ _HashInfo(
174
+ type=hashlib.md5,
175
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/md5/md5.h#L100
176
+ stateSize=92,
177
+ # Found empirically
178
+ initVectorFirstWord=b'\x01\x23\x45\x67\x89\xab\xcd\xef'
179
+ )
180
+ _HashInfo(
181
+ type=hashlib.sha1,
182
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha.h#L100
183
+ stateSize=96,
184
+ # Found empirically
185
+ initVectorFirstWord=b'\x01\x23\x45\x67\x89\xab\xcd\xef'
186
+ )
187
+ _HashInfo(
188
+ type=hashlib.sha224,
189
+ # Uses the same struct as sha256
190
+ stateSize=112,
191
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha256.c#L22
192
+ initVectorFirstWord=b'\xd8\x9e\x05\xc1\x07\xd5\x7c\x36'
193
+ )
194
+ _HashInfo(
195
+ type=hashlib.sha256,
196
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha.h#L135
197
+ stateSize=112,
198
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha256.c#L33
199
+ initVectorFirstWord=b'\x67\xe6\x09\x6a\x85\xae\x67\xbb'
200
+ )
201
+ _HashInfo(
202
+ type=hashlib.sha384,
203
+ # Uses the same struct as sha512
204
+ stateSize=216,
205
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha512.c#L64
206
+ initVectorFirstWord=b'\xd8\x9e\x05\xc1\x5d\x9d\xbb\xcb'
207
+ )
208
+ _HashInfo(
209
+ type=hashlib.sha512,
210
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha.h#L182
211
+ stateSize=216,
212
+ # github.com/openssl/openssl/blob/OpenSSL_1_0_1f/crypto/sha/sha512.c#L80
213
+ initVectorFirstWord=b'\x08\xc9\xbc\xf3\x67\xe6\x09\x6a'
214
+ )
215
+
216
+
217
+ def serialize(hashObject):
218
+ """
219
+ Serializes the internal state of the hash object passed in. Calling
220
+ restore() on the serialized value afterward will return an equivalent
221
+ hash object to the one passed.
222
+
223
+ :param hashObject: The hash object to serialize.
224
+ :type hashObject: _hashlib.HASH
225
+ :returns: A binary string representing the serialized state.
226
+ """
227
+ hashInfo = _HASH_INFOS[hashObject.name]
228
+ stateDataPointer = _getStateDataPointer(hashObject, hashInfo)
229
+ return stateDataPointer[:hashInfo.stateSize]
230
+
231
+
232
+ def restore(oldStateData, hashName):
233
+ """
234
+ Unserializes a hash object from a binary string created by the
235
+ serialize() method in this module.
236
+
237
+ :param oldStateData: The serialized state.
238
+ :type oldStateData: binary string
239
+ :param hashName: The name of the hash algorithm type.
240
+ :type hashName: str
241
+ :returns: A hash object in the same state as the one that serialize()
242
+ was called on.
243
+ """
244
+ hashInfo = _HASH_INFOS[hashName]
245
+ assert len(oldStateData) == hashInfo.stateSize
246
+
247
+ hashObject = hashInfo.type()
248
+ stateDataPointer = _getStateDataPointer(hashObject, hashInfo)
249
+
250
+ for i, byte in enumerate(oldStateData):
251
+ stateDataPointer[i] = byte
252
+
253
+ return hashObject
254
+
255
+
256
+ def serializeHex(hashObject):
257
+ return binascii.b2a_hex(serialize(hashObject))
258
+
259
+
260
+ def restoreHex(oldHexStateData, hashName):
261
+ return restore(binascii.a2b_hex(oldHexStateData), hashName)
@@ -33,7 +33,7 @@ export default async (_config: PlaywrightTestConfig) => {
33
33
 
34
34
  report.execute(context);
35
35
 
36
- const outputDir = '../../build/test/coverage';
36
+ const outputDir = process.env.GIRDER_TEST_COVERAGE_DIR || '../../build/test/coverage';
37
37
  await fs.mkdir(outputDir, { recursive: true });
38
38
  const contextXml = libReport.createContext({
39
39
  dir: outputDir,
@@ -41,7 +41,7 @@ export default async (_config: PlaywrightTestConfig) => {
41
41
  coverageMap,
42
42
  });
43
43
  const xmlReport = reports.create('cobertura', {
44
- file: 'cobertura-coverage.xml',
44
+ file: process.env.GIRDER_TEST_COVERAGE_FILE || 'cobertura-coverage.xml',
45
45
  skipEmpty: false,
46
46
  });
47
47
  xmlReport.execute(contextXml);
@@ -0,0 +1,100 @@
1
+ import { expect, Page, test } from '@playwright/test';
2
+
3
+ import { setupServer } from '../server';
4
+ import { createUser, waitForIdlePage } from '../util';
5
+
6
+ /**
7
+ * Ported from 3.x-maintenance: plugins/autojoin/plugin_tests/autojoinSpec.js
8
+ * Tests for the Auto Join plugin's configuration page and its effect on new
9
+ * users.
10
+ */
11
+
12
+ /**
13
+ * Navigate to the groups list page.
14
+ */
15
+ async function gotoGroupsPage(page: Page) {
16
+ await page.locator('a.g-nav-link[g-target="groups"]').click();
17
+ await expect(page.locator('.g-group-search-form .g-search-field')).toBeVisible();
18
+ }
19
+
20
+ /**
21
+ * Create a private group and wait for its page to load. Assumes the user is
22
+ * logged in.
23
+ */
24
+ async function createGroup(page: Page, name: string) {
25
+ await gotoGroupsPage(page);
26
+ await expect(page.locator('.g-group-create-button')).toBeVisible();
27
+ await page.locator('.g-group-create-button').click();
28
+ await expect(page.locator('#g-name')).toBeVisible();
29
+ await page.locator('#g-name').fill(name);
30
+ await page.locator('.g-save-group').click();
31
+ await waitForIdlePage(page);
32
+ await expect(page.locator('.g-group-name')).toHaveText(name);
33
+ }
34
+
35
+ /**
36
+ * Navigate to the Auto Join plugin configuration page.
37
+ */
38
+ async function gotoAutojoinConfig(page: Page) {
39
+ await page.locator('a.g-nav-link[g-target="admin"]').click();
40
+ await page.locator('.g-plugins-config').click();
41
+ const configLink = page.locator('.g-plugin-list-item[data-name="autojoin"] a.g-plugin-config-link');
42
+ await expect(configLink).toBeVisible();
43
+ await configLink.click();
44
+ // Wait for the group collection to populate the group select.
45
+ await expect(page.locator('#g-autojoin-group > option')).toHaveCount(4);
46
+ }
47
+
48
+ /**
49
+ * Fill out and submit the "add rule" row of the Auto Join configuration page.
50
+ */
51
+ async function addRule(page: Page, pattern: string, groupName: string, level: string) {
52
+ await page.locator('#g-autojoin-pattern').fill(pattern);
53
+ await page.locator('#g-autojoin-group').selectOption({ label: groupName });
54
+ await page.locator('#g-autojoin-level').selectOption(level);
55
+ await page.locator('#g-autojoin-add').click();
56
+ }
57
+
58
+ test.describe('Test the auto join plugin', () => {
59
+ setupServer();
60
+
61
+ test('configure auto join rules', async ({ page }) => {
62
+ await createUser(page, 'admin', 'admin@girder.test', 'Joe', 'Admin', 'password');
63
+
64
+ await createGroup(page, 'group1');
65
+ await createGroup(page, 'group2');
66
+ await createGroup(page, 'group3');
67
+
68
+ await gotoAutojoinConfig(page);
69
+
70
+ await addRule(page, '@girder1.test', 'group1', '2');
71
+ await addRule(page, '@girder2.test', 'group2', '1');
72
+ await addRule(page, '@girder2.test', 'group3', '0');
73
+
74
+ await expect(page.locator('.g-autojoin-container tbody tr')).toHaveCount(3);
75
+ await expect(page.locator('.g-autojoin-container')).toContainText('@girder1.test');
76
+ await expect(page.locator('.g-autojoin-container')).toContainText('@girder2.test');
77
+
78
+ await page.locator('#g-autojoin-save').click();
79
+ await expect(page.locator('#g-alerts-container')).toContainText('Settings saved');
80
+ await waitForIdlePage(page);
81
+ });
82
+
83
+ test('auto join a user whose email matches the girder2 rule', async ({ page }) => {
84
+ await createUser(page, 'user1', 'user1@girder2.test', 'Joe', 'User', 'password');
85
+
86
+ await gotoGroupsPage(page);
87
+ await expect(page.locator('.g-group-title', { hasText: 'group1' })).toHaveCount(0);
88
+ await expect(page.locator('.g-group-title', { hasText: 'group2' })).toHaveCount(1);
89
+ await expect(page.locator('.g-group-title', { hasText: 'group3' })).toHaveCount(1);
90
+ });
91
+
92
+ test('auto join a user whose email matches the girder1 rule', async ({ page }) => {
93
+ await createUser(page, 'user2', 'user2@girder1.test', 'Joe', 'User', 'password');
94
+
95
+ await gotoGroupsPage(page);
96
+ await expect(page.locator('.g-group-title', { hasText: 'group1' })).toHaveCount(1);
97
+ await expect(page.locator('.g-group-title', { hasText: 'group2' })).toHaveCount(0);
98
+ await expect(page.locator('.g-group-title', { hasText: 'group3' })).toHaveCount(0);
99
+ });
100
+ });
@@ -0,0 +1,74 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ import { Page, expect, test } from '@playwright/test';
5
+
6
+ import { setupServer } from '../server';
7
+ import { createUser, upload, waitForIdlePage } from '../util';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ /**
13
+ * Make a REST request from within the page context and await its result.
14
+ */
15
+ async function asyncRestRequest(page: Page, opts: Record<string, unknown>) {
16
+ return await page.evaluate((requestOpts) => {
17
+ return new Promise((resolve, reject) => {
18
+ // @ts-ignore - window.girder is available at runtime
19
+ window.girder.rest.restRequest(requestOpts).done((resp: unknown) => {
20
+ resolve(resp);
21
+ }).fail((resp: unknown) => {
22
+ reject(resp);
23
+ });
24
+ });
25
+ }, opts);
26
+ }
27
+
28
+ test.describe('Test the hashsum download front-end', () => {
29
+ setupServer();
30
+
31
+ test('verify presence of key file', async ({ page }) => {
32
+ await createUser(page, 'admin');
33
+ await page.locator('#g-app-header-container').getByText('admin').click();
34
+ await page.locator('a.g-my-folders').click();
35
+ await page.getByRole('link', { name: ' Private ' }).click();
36
+ await upload(page, path.join(__dirname, 'data', 'ten_byte_file.txt'));
37
+
38
+ await page.getByRole('link', { name: ' ten_byte_file.txt' }).click();
39
+ await page.getByTitle('Show info').click();
40
+ await expect(page.locator('input.g-hash-textbox').first()).toHaveValue('ff3245abe317049ed1b8aa7aa2f4c4dcb8bf86f083ed67eb26b43e2fbe3ba8fdf759f9e2f46fcf2a06c2dfeddf0cedcd41a68034cd618b880785b34f759d1a69')
41
+ });
42
+ });
43
+
44
+ test.describe('Test the hashsum download configuration page', () => {
45
+ setupServer();
46
+
47
+ test('enable automatic checksum computation', async ({ page }) => {
48
+ await createUser(page, 'admin');
49
+
50
+ // Navigate to the plugin's admin settings page.
51
+ await page.locator('a.g-nav-link[g-target="admin"]').click();
52
+ await page.locator('.g-plugins-config').click();
53
+ const configLink = page.locator('.g-plugin-list-item[data-name="hashsum_download"] a.g-plugin-config-link');
54
+ await expect(configLink).toBeVisible();
55
+ await configLink.click();
56
+
57
+ const autoCompute = page.locator('#g-hashsum-download-auto-compute');
58
+ await expect(autoCompute).toBeVisible();
59
+ await expect(autoCompute).not.toBeChecked();
60
+ await autoCompute.check();
61
+
62
+ await page.locator('#g-hashsum-download-config-form input[type="submit"]').click();
63
+ await expect(page.locator('#g-alerts-container')).toContainText('Settings saved');
64
+ await waitForIdlePage(page);
65
+
66
+ // Verify that the setting was persisted on the server.
67
+ const settings = await asyncRestRequest(page, {
68
+ url: '/system/setting',
69
+ method: 'GET',
70
+ data: { list: JSON.stringify(['hashsum_download.auto_compute']) },
71
+ }) as Record<string, unknown>;
72
+ expect(settings['hashsum_download.auto_compute']).toBe(true);
73
+ });
74
+ });
@@ -0,0 +1,118 @@
1
+ import { Locator, Page, expect, test } from '@playwright/test';
2
+
3
+ import { setupServer } from '../server';
4
+ import { createUser, login, logout } from '../util';
5
+
6
+ /**
7
+ * Ported from 3.x-maintenance: plugins/homepage/plugin_tests/homepageSpec.js
8
+ * Tests for the Homepage plugin's configuration page and its effect on the
9
+ * front page.
10
+ */
11
+
12
+ const markdown =
13
+ 'It\'s very easy to make some words **bold** and other words *italic* with ' +
14
+ 'Markdown. You can even [link to Girder!](https://girder.readthedocs.io/)';
15
+
16
+ /**
17
+ * Assert that the given container renders the expected markdown content.
18
+ */
19
+ async function expectMarkdown(container: Locator) {
20
+ await expect(container.locator('p')).toContainText("It's very easy");
21
+ await expect(container.locator('strong')).toContainText('bold');
22
+ await expect(container.locator('em')).toContainText('italic');
23
+ await expect(container.locator('a[href="https://girder.readthedocs.io/"]')).toContainText('link to Girder!');
24
+ }
25
+
26
+ /**
27
+ * Navigate to the Homepage plugin configuration page.
28
+ */
29
+ async function gotoHomepageConfig(page: Page) {
30
+ await page.locator('a.g-nav-link[g-target="admin"]').click();
31
+ await page.locator('.g-plugins-config').click();
32
+ const configLink = page.locator('.g-plugin-list-item[data-name="homepage"] a.g-plugin-config-link');
33
+ await expect(configLink).toBeVisible();
34
+ await configLink.click();
35
+ await expect(page.locator('.g-homepage-container')).toBeVisible();
36
+ }
37
+
38
+ /**
39
+ * Navigate back to the front page via the app title.
40
+ */
41
+ async function gotoFrontPage(page: Page) {
42
+ await page.locator('.g-app-title').click();
43
+ }
44
+
45
+ /**
46
+ * Save the homepage configuration form and wait for the success alert.
47
+ */
48
+ async function saveHomepageConfig(page: Page) {
49
+ await page.locator('#g-homepage-form input[type="submit"]').click();
50
+ await expect(page.locator('#g-alerts-container')).toContainText('Settings saved');
51
+ }
52
+
53
+ test.describe('Test the homepage front-end', () => {
54
+ setupServer();
55
+
56
+ test('exercise homepage markdown customization', async ({ page }) => {
57
+ await createUser(page, 'admin', 'admin@girder.test', 'Mark', 'Down', 'password');
58
+
59
+ await gotoHomepageConfig(page);
60
+
61
+ // Write markdown and verify the preview renders it.
62
+ await page.locator('.g-homepage-container textarea.g-markdown-text').fill(markdown);
63
+ await page.locator('.g-homepage-container a.g-preview-link').click();
64
+ await expectMarkdown(page.locator('.g-homepage-container .g-markdown-preview'));
65
+
66
+ await saveHomepageConfig(page);
67
+
68
+ // The front page body is completely replaced by the rendered markdown.
69
+ await gotoFrontPage(page);
70
+ await expectMarkdown(page.locator('#g-app-body-container'));
71
+ });
72
+
73
+ test('set, preview, and save welcome text and branding', async ({ page }) => {
74
+ await login(page, 'admin', 'password');
75
+
76
+ await gotoHomepageConfig(page);
77
+
78
+ // Clear the full-page markdown so the branding settings are used.
79
+ await page.locator('.g-homepage-container textarea.g-markdown-text').fill('');
80
+ await page.locator('.g-homepage-container a.g-preview-link').click();
81
+ await expect(page.locator('.g-homepage-container .g-markdown-preview')).toHaveText('Nothing to show');
82
+
83
+ // Set header, subheader, and welcome text.
84
+ await page.locator('#g-homepage-header').fill('Header');
85
+ await page.locator('#g-homepage-subheader').fill('Subheader');
86
+ const welcomeText = page.locator('.g-homepage-welcome-text-container textarea.g-markdown-text');
87
+ await welcomeText.fill(markdown);
88
+ await page.locator('.g-homepage-welcome-text-container a.g-preview-link').click();
89
+ await expectMarkdown(page.locator('.g-homepage-welcome-text-container .g-markdown-preview'));
90
+
91
+ await saveHomepageConfig(page);
92
+
93
+ // The branded front page shows the configured header/subheader and the
94
+ // welcome text rendered as markdown.
95
+ await gotoFrontPage(page);
96
+ await expect(page.locator('.g-frontpage-title')).toHaveText('Header');
97
+ await expect(page.locator('.g-frontpage-subtitle')).toHaveText('Subheader');
98
+ await expectMarkdown(page.locator('.g-frontpage-welcome-text-content'));
99
+ });
100
+
101
+ test('settings persist after reload and are visible anonymously', async ({ page }) => {
102
+ await login(page, 'admin', 'password');
103
+
104
+ // Configure the full-page markdown replacement.
105
+ await gotoHomepageConfig(page);
106
+ await page.locator('.g-homepage-container textarea.g-markdown-text').fill(markdown);
107
+ await saveHomepageConfig(page);
108
+
109
+ // Re-open the config page and verify the markdown was persisted.
110
+ await gotoHomepageConfig(page);
111
+ await expect(page.locator('.g-homepage-container textarea.g-markdown-text')).toHaveValue(markdown);
112
+
113
+ // Log out and verify the markdown still renders to anonymous users.
114
+ await logout(page);
115
+ await gotoFrontPage(page);
116
+ await expectMarkdown(page.locator('#g-app-body-container'));
117
+ });
118
+ });