girder 5.0.19.dev60__tar.gz → 5.0.19.dev74__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 (289) hide show
  1. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/config.yml +27 -7
  2. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.pre-commit-config.yaml +1 -0
  3. {girder-5.0.19.dev60/girder.egg-info → girder-5.0.19.dev74}/PKG-INFO +1 -1
  4. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/playwright-teardown.ts +2 -2
  5. girder-5.0.19.dev74/girder/web/tests/spec/plugin_autojoin.spec.ts +100 -0
  6. girder-5.0.19.dev74/girder/web/tests/spec/plugin_hashsum_download.spec.ts +74 -0
  7. girder-5.0.19.dev74/girder/web/tests/spec/plugin_homepage.spec.ts +118 -0
  8. girder-5.0.19.dev74/girder/web/tests/spec/plugin_item_licenses.spec.ts +257 -0
  9. girder-5.0.19.dev74/girder/web/tests/spec/plugin_jobs.spec.ts +562 -0
  10. girder-5.0.19.dev74/girder/web/tests/spec/plugin_readme.spec.ts +52 -0
  11. girder-5.0.19.dev74/girder/web/tests/spec/plugin_terms.spec.ts +263 -0
  12. girder-5.0.19.dev74/girder/web/tests/spec/plugin_user_quota.spec.ts +434 -0
  13. girder-5.0.19.dev74/girder/web/tests/spec/utilities.spec.ts +434 -0
  14. girder-5.0.19.dev74/girder/web/tests/spec/version.spec.ts +60 -0
  15. girder-5.0.19.dev74/girder/web/tests/spec/widgets.spec.ts +290 -0
  16. {girder-5.0.19.dev60 → girder-5.0.19.dev74/girder.egg-info}/PKG-INFO +1 -1
  17. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/SOURCES.txt +5 -0
  18. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/scm_file_list.json +5 -0
  19. girder-5.0.19.dev74/girder.egg-info/scm_version.json +8 -0
  20. girder-5.0.19.dev60/girder/web/tests/spec/plugin_hashsum_download.spec.ts +0 -26
  21. girder-5.0.19.dev60/girder/web/tests/spec/plugin_homepage.spec.ts +0 -37
  22. girder-5.0.19.dev60/girder/web/tests/spec/plugin_item_licenses.spec.ts +0 -26
  23. girder-5.0.19.dev60/girder/web/tests/spec/plugin_readme.spec.ts +0 -24
  24. girder-5.0.19.dev60/girder/web/tests/spec/plugin_terms.spec.ts +0 -38
  25. girder-5.0.19.dev60/girder/web/tests/spec/plugin_user_quota.spec.ts +0 -39
  26. girder-5.0.19.dev60/girder.egg-info/scm_version.json +0 -8
  27. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/Dockerfile +0 -0
  28. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/build_plugins.py +0 -0
  29. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/generatePyEnvChecksum.sh +0 -0
  30. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/publish_npm.sh +0 -0
  31. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/publish_pypi.sh +0 -0
  32. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.circleci/rabbitmq.conf +0 -0
  33. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.codecov.yml +0 -0
  34. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.dockerignore +0 -0
  35. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.editorconfig +0 -0
  36. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.git-blame-ignore-revs +0 -0
  37. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.github/workflows/codespell.yml +0 -0
  38. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.gitignore +0 -0
  39. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/.readthedocs.yml +0 -0
  40. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/CHANGELOG.rst +0 -0
  41. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/CMakeLists.txt +0 -0
  42. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/CONTRIBUTING.rst +0 -0
  43. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/Dockerfile +0 -0
  44. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/LICENSE +0 -0
  45. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/MANIFEST.in +0 -0
  46. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/README.rst +0 -0
  47. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/README.rst +0 -0
  48. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/girder_client/__init__.py +0 -0
  49. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/girder_client/_jsonemitter.py +0 -0
  50. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/girder_client/cli.py +0 -0
  51. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/pyproject.toml +0 -0
  52. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/clients/python/setup.py +0 -0
  53. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docker-compose.yml +0 -0
  54. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/admin-docs.rst +0 -0
  55. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/api-docs.rst +0 -0
  56. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/build-docs.rst +0 -0
  57. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/conf.py +0 -0
  58. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/configuration.rst +0 -0
  59. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/dependencies.rst +0 -0
  60. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/deployment.rst +0 -0
  61. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/dev-installation.rst +0 -0
  62. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/developer-cookbook.rst +0 -0
  63. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/developer-docs.rst +0 -0
  64. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/development.rst +0 -0
  65. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/favicon.ico +0 -0
  66. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/images/dicom-viewer.png +0 -0
  67. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/index.rst +0 -0
  68. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/license.png +0 -0
  69. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/list_env_settings.py +0 -0
  70. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/list_env_settings.yaml +0 -0
  71. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/migration-guide.rst +0 -0
  72. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/mount.rst +0 -0
  73. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/plugin-development.rst +0 -0
  74. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/plugins.rst +0 -0
  75. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/python-client.rst +0 -0
  76. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/requirements-docs.txt +0 -0
  77. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/security.rst +0 -0
  78. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/sftp.rst +0 -0
  79. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/static/custom.css +0 -0
  80. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/user-docs.rst +0 -0
  81. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/docs/user-guide.rst +0 -0
  82. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/__init__.py +0 -0
  83. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/__init__.py +0 -0
  84. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/access.py +0 -0
  85. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/api_docs.mako +0 -0
  86. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/api_main.py +0 -0
  87. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/describe.py +0 -0
  88. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/docs.py +0 -0
  89. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/filter_logging.py +0 -0
  90. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/rest.py +0 -0
  91. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/sftp.py +0 -0
  92. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/__init__.py +0 -0
  93. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/api_key.py +0 -0
  94. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/assetstore.py +0 -0
  95. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/collection.py +0 -0
  96. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/file.py +0 -0
  97. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/folder.py +0 -0
  98. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/group.py +0 -0
  99. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/item.py +0 -0
  100. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/resource.py +0 -0
  101. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/system.py +0 -0
  102. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/token.py +0 -0
  103. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/api/v1/user.py +0 -0
  104. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/asgi.py +0 -0
  105. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/cli/__init__.py +0 -0
  106. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/cli/mount.py +0 -0
  107. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/cli/serve.py +0 -0
  108. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/cli/sftpd.py +0 -0
  109. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/cli/shell.py +0 -0
  110. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/constants.py +0 -0
  111. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/events.py +0 -0
  112. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/exceptions.py +0 -0
  113. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/_footer.mako +0 -0
  114. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/_header.mako +0 -0
  115. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/accountApproval.mako +0 -0
  116. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/accountApproved.mako +0 -0
  117. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/emailVerification.mako +0 -0
  118. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/groupInvite.mako +0 -0
  119. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/mail_templates/temporaryAccess.mako +0 -0
  120. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/__init__.py +0 -0
  121. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/api_key.py +0 -0
  122. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/assetstore.py +0 -0
  123. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/collection.py +0 -0
  124. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/file.py +0 -0
  125. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/folder.py +0 -0
  126. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/group.py +0 -0
  127. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/item.py +0 -0
  128. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/model_base.py +0 -0
  129. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/setting.py +0 -0
  130. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/token.py +0 -0
  131. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/upload.py +0 -0
  132. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/models/user.py +0 -0
  133. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/notification.py +0 -0
  134. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/plugin.py +0 -0
  135. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/settings.py +0 -0
  136. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/tasks.py +0 -0
  137. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/__init__.py +0 -0
  138. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/_cache.py +0 -0
  139. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/_hash_state.py +0 -0
  140. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/abstract_assetstore_adapter.py +0 -0
  141. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/acl_mixin.py +0 -0
  142. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/assetstore_utilities.py +0 -0
  143. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/config.py +0 -0
  144. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/error.mako +0 -0
  145. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/filesystem_assetstore_adapter.py +0 -0
  146. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/mail_utils.py +0 -0
  147. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/model_importer.py +0 -0
  148. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/path.py +0 -0
  149. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/progress.py +0 -0
  150. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/s3_assetstore_adapter.py +0 -0
  151. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/search.py +0 -0
  152. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/server.py +0 -0
  153. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/setting_utilities.py +0 -0
  154. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/system.py +0 -0
  155. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/webroot.py +0 -0
  156. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/utility/ziputil.py +0 -0
  157. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/.env.development +0 -0
  158. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/.gitignore +0 -0
  159. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/.prettierrc.json +0 -0
  160. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/README.rst +0 -0
  161. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/Girder_Favicon.png +0 -0
  162. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/Girder_Mark.png +0 -0
  163. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/fontello-BGLM866c.svg +0 -0
  164. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/fontello-CMxI46LA.eot +0 -0
  165. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/fontello-Ddaw8Z2O.woff2 +0 -0
  166. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/fontello-KuItqCW9.ttf +0 -0
  167. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/fontello-_lT35XxX.woff +0 -0
  168. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/glyphicons-halflings-regular-BKjkU69z.woff +0 -0
  169. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/glyphicons-halflings-regular-BUJKDMgK.eot +0 -0
  170. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/glyphicons-halflings-regular-CSehLiBc.svg +0 -0
  171. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/glyphicons-halflings-regular-DrwTMapi.ttf +0 -0
  172. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/glyphicons-halflings-regular-DzqM6ju8.woff2 +0 -0
  173. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/index-C3KtNRrl.js +0 -0
  174. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/index-C3KtNRrl.js.map +0 -0
  175. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/index-DFhDyJFH.css +0 -0
  176. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/jsoneditor-icons-CIlYC0TY.svg +0 -0
  177. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-300-B6tDWRxp.woff2 +0 -0
  178. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-300-DZbNTRe6.woff +0 -0
  179. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-300italic-DLsXqxhU.woff2 +0 -0
  180. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-300italic-Dfz31Up5.woff +0 -0
  181. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-400-D5zbb_EN.woff +0 -0
  182. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-400-DLgeRt71.woff2 +0 -0
  183. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-400italic-DpGQ6Ktj.woff2 +0 -0
  184. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-400italic-UJbezL8h.woff +0 -0
  185. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-600-CCvT4JHQ.woff2 +0 -0
  186. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-600-DHWfDY-G.woff +0 -0
  187. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-600italic-X8kCjSq_.woff +0 -0
  188. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-600italic-slqXzGub.woff2 +0 -0
  189. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-700-CsbQbrjx.woff2 +0 -0
  190. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-700-DNl7WOH5.woff +0 -0
  191. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-700italic-BtE7VvhE.woff2 +0 -0
  192. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-700italic-hc2P_kZo.woff +0 -0
  193. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-800-CbJOM4_m.woff2 +0 -0
  194. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-800-XaYTAPrv.woff +0 -0
  195. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-800italic-DqvYew-K.woff +0 -0
  196. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/assets/open-sans-latin-800italic-JIGkTxpX.woff2 +0 -0
  197. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/index.html +0 -0
  198. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/auth.js +0 -0
  199. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/constants.js +0 -0
  200. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/dialog.js +0 -0
  201. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/events.js +0 -0
  202. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/index.ts +0 -0
  203. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/main.ts +0 -0
  204. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/misc.js +0 -0
  205. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/pluginUtils.js +0 -0
  206. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/rest.js +0 -0
  207. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/router.js +0 -0
  208. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/routes.js +0 -0
  209. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/version.js +0 -0
  210. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/src/src/vite-env.d.ts +0 -0
  211. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/swagger-ui/swagger-ui-bundle.js +0 -0
  212. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/dist/swagger-ui/swagger-ui.css +0 -0
  213. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/env.d.ts +0 -0
  214. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/playwright.config.ts +0 -0
  215. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/coverage.ts +0 -0
  216. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/playwright-setup.ts +0 -0
  217. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/server.ts +0 -0
  218. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/admin.spec.ts +0 -0
  219. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/browser.spec.ts +0 -0
  220. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/collection.spec.ts +0 -0
  221. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/collectionBaseClass.spec.ts +0 -0
  222. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/customWidgets.spec.ts +0 -0
  223. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/README.md +0 -0
  224. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/fake.jpg +0 -0
  225. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/nomime.dat +0 -0
  226. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/slicer_cli_web_multiple_test.xml +0 -0
  227. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/slicer_cli_web_test.xml +0 -0
  228. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/ten_byte_file.txt +0 -0
  229. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/testFile.txt +0 -0
  230. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data/testFile2 +0 -0
  231. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/data.spec.ts +0 -0
  232. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/emptyLayout.spec.ts +0 -0
  233. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/file_upload.spec.ts +0 -0
  234. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/folder.spec.ts +0 -0
  235. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/group.spec.ts +0 -0
  236. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/item.spec.ts +0 -0
  237. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/model.spec.ts +0 -0
  238. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_authorized_upload.spec.ts +0 -0
  239. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web.spec.ts +0 -0
  240. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_config_view.spec.ts +0 -0
  241. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_container_view.spec.ts +0 -0
  242. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_item_selector.spec.ts +0 -0
  243. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_panel_group.spec.ts +0 -0
  244. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_parser.spec.ts +0 -0
  245. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_task.spec.ts +0 -0
  246. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_widget_model.spec.ts +0 -0
  247. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_slicer_cli_web_widget_view.spec.ts +0 -0
  248. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/plugin_thumbnails.spec.ts +0 -0
  249. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/routing.spec.ts +0 -0
  250. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/setApi.spec.ts +0 -0
  251. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/setting_show_download.spec.ts +0 -0
  252. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/sortWidget.spec.ts +0 -0
  253. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/swagger.spec.ts +0 -0
  254. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/spec/user.spec.ts +0 -0
  255. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/web/tests/util.ts +0 -0
  256. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/worker_plugin.py +0 -0
  257. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder/wsgi.py +0 -0
  258. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/dependency_links.txt +0 -0
  259. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/entry_points.txt +0 -0
  260. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/not-zip-safe +0 -0
  261. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/requires.txt +0 -0
  262. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/girder.egg-info/top_level.txt +0 -0
  263. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/jsdoc.json +0 -0
  264. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/package-lock.json +0 -0
  265. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/package.json +0 -0
  266. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pyproject.toml +0 -0
  267. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/LICENSE +0 -0
  268. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/MANIFEST.in +0 -0
  269. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pyproject.toml +0 -0
  270. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/__init__.py +0 -0
  271. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/assertions.py +0 -0
  272. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/fixtures.py +0 -0
  273. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/plugin.py +0 -0
  274. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/plugin_registry.py +0 -0
  275. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/utils.py +0 -0
  276. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/pytest_girder/web_client.py +0 -0
  277. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/pytest_girder/setup.py +0 -0
  278. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/requirements-dev.txt +0 -0
  279. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/midas/README.rst +0 -0
  280. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/midas/migrate.py +0 -0
  281. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/midas/requirements.txt +0 -0
  282. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/midas/walk_girder.py +0 -0
  283. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/midas/walk_midas.py +0 -0
  284. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/publicNames.py +0 -0
  285. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/publicNames.txt +0 -0
  286. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/scripts/test_names.sh +0 -0
  287. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/setup.cfg +0 -0
  288. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/setup.py +0 -0
  289. {girder-5.0.19.dev60 → girder-5.0.19.dev74}/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.dev74
4
4
  Summary: Web-based data management platform
5
5
  Home-page: https://girder.readthedocs.org
6
6
  Author: Kitware, Inc.
@@ -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
+ });
@@ -0,0 +1,257 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+
4
+ import { Locator, Page, expect, test } from '@playwright/test';
5
+
6
+ import { setupServer } from '../server';
7
+ import { createUser, login, waitForDialog, waitForIdlePage } from '../util';
8
+
9
+ /**
10
+ * Ported from 3.x-maintenance:
11
+ * plugins/item_licenses/plugin_tests/itemLicensesSpec.js
12
+ *
13
+ * Tests the item_licenses plugin: selecting a license when creating/editing an
14
+ * item or uploading a file, viewing the license on the item page, and the
15
+ * behavior of the "Unspecified" license.
16
+ *
17
+ * The original spec was a single sequential Jasmine describe. This port keeps
18
+ * the same sequence in a serial Playwright describe: the server and database
19
+ * are shared across the whole describe, admin is registered in the first test,
20
+ * and later tests log back in as needed.
21
+ */
22
+
23
+ const __filename = fileURLToPath(import.meta.url);
24
+ const __dirname = path.dirname(__filename);
25
+
26
+ const ADMIN = {
27
+ login: 'admin',
28
+ email: 'admin@girder.test',
29
+ firstName: 'Admin',
30
+ lastName: 'User',
31
+ password: 'password!',
32
+ };
33
+
34
+ const MIT_LICENSE = 'The MIT License (MIT)';
35
+ const APACHE_LICENSE = 'Apache License 2';
36
+
37
+ /** The number of license options: 12 code + 9 content, plus "Unspecified". */
38
+ const NUM_LICENSES = 22;
39
+
40
+ /**
41
+ * Navigate to the admin's folder list.
42
+ */
43
+ async function gotoMyFolders(page: Page) {
44
+ await expect(page.locator('.g-user-dropdown-link')).toBeVisible();
45
+ await page.locator('.g-user-dropdown-link').click();
46
+ await page.locator('a.g-my-folders').click();
47
+ await expect(page.locator('a.g-folder-list-link', { hasText: 'Public' })).toBeVisible();
48
+ }
49
+
50
+ /**
51
+ * Open a folder of the current user's page by name.
52
+ */
53
+ async function openFolder(page: Page, name: string) {
54
+ await expect(page.locator('a.g-folder-list-link', { hasText: name })).toBeVisible();
55
+ await page.locator('a.g-folder-list-link', { hasText: name }).click();
56
+ await expect(page.locator('.g-folder-actions-button:visible')).toHaveCount(1);
57
+ await waitForIdlePage(page);
58
+ }
59
+
60
+ /**
61
+ * Assert that the license select in the currently open dialog has the expected
62
+ * options and selected value.
63
+ */
64
+ async function expectLicenseSelect(select: Locator, currentLicense: string) {
65
+ await expect(select).toBeVisible();
66
+ await expect(select.locator('optgroup')).toHaveCount(2);
67
+ await expect(select.locator('option')).toHaveCount(NUM_LICENSES);
68
+ await expect(select).toHaveValue(currentLicense);
69
+ }
70
+
71
+ /**
72
+ * Open the "Create item here" dialog from the folder actions menu.
73
+ */
74
+ async function openCreateItemDialog(page: Page) {
75
+ await page.locator('.g-folder-actions-button:visible').click();
76
+ await expect(page.locator('a.g-create-item:visible')).toHaveCount(1);
77
+ await page.locator('a.g-create-item:visible').click();
78
+ await expect.poll(() => page.evaluate(() => window.location.hash)).toContain('dialog=itemcreate');
79
+ await waitForDialog(page);
80
+ await expect(page.locator('.g-save-item:visible')).toHaveText('Create');
81
+ }
82
+
83
+ /**
84
+ * Open the item edit dialog from the item actions menu.
85
+ */
86
+ async function openEditItemDialog(page: Page) {
87
+ await expect(page.locator('.g-item-actions-button:visible')).toHaveCount(1);
88
+ await page.locator('.g-item-actions-button:visible').click();
89
+ await expect(page.locator('.g-edit-item:visible')).toHaveCount(1);
90
+ await page.locator('.g-edit-item:visible').click();
91
+ await expect.poll(() => page.evaluate(() => window.location.hash)).toContain('dialog=itemedit');
92
+ await waitForDialog(page);
93
+ await expect(page.locator('#g-name')).not.toHaveValue('');
94
+ await expect(page.locator('.g-save-item:visible')).toHaveText('Save');
95
+ }
96
+
97
+ /**
98
+ * Open an item from the current folder listing and return once the item page's
99
+ * license field has loaded.
100
+ */
101
+ async function openItem(page: Page, name: string) {
102
+ await expect(page.locator('a.g-item-list-link', { hasText: name })).toHaveCount(1);
103
+ await page.locator('a.g-item-list-link', { hasText: name }).click();
104
+ await expect(page.locator('.g-item-name')).toHaveText(name);
105
+ await expect(page.locator('.g-item-license')).toHaveCount(1);
106
+ await waitForIdlePage(page);
107
+ }
108
+
109
+ /**
110
+ * Navigate back to the Public folder listing via the breadcrumb.
111
+ */
112
+ async function backToPublicFolder(page: Page) {
113
+ await expect(page.locator('a.g-item-breadcrumb-link', { hasText: 'Public' })).toBeVisible();
114
+ await page.locator('a.g-item-breadcrumb-link', { hasText: 'Public' }).click();
115
+ await expect(page.locator('.g-folder-actions-button:visible')).toHaveCount(1);
116
+ await waitForIdlePage(page);
117
+ }
118
+
119
+ test.describe('Test the item licenses front-end', () => {
120
+ setupServer();
121
+
122
+ test('register the admin user', async ({ page }) => {
123
+ await createUser(page, ADMIN.login, ADMIN.email, ADMIN.firstName, ADMIN.lastName, ADMIN.password);
124
+ });
125
+
126
+ test('create an item with a license', async ({ page }) => {
127
+ await login(page, ADMIN.login, ADMIN.password);
128
+ await gotoMyFolders(page);
129
+ await openFolder(page, 'Public');
130
+ await openCreateItemDialog(page);
131
+
132
+ // "Unspecified" is selected by default.
133
+ await expectLicenseSelect(page.locator('#g-license'), '');
134
+
135
+ await page.locator('input#g-name').fill('Test Item');
136
+ await page.locator('#g-license').selectOption(MIT_LICENSE);
137
+ await page.locator('.g-save-item').click();
138
+ await waitForIdlePage(page);
139
+
140
+ await openItem(page, 'Test Item');
141
+ await expect(page.locator('.g-item-license')).toContainText(MIT_LICENSE);
142
+ });
143
+
144
+ test('edit an item license', async ({ page }) => {
145
+ await login(page, ADMIN.login, ADMIN.password);
146
+ await gotoMyFolders(page);
147
+ await openFolder(page, 'Public');
148
+ await openItem(page, 'Test Item');
149
+
150
+ await openEditItemDialog(page);
151
+ // The item's current license is selected by default.
152
+ await expectLicenseSelect(page.locator('#g-license'), MIT_LICENSE);
153
+
154
+ await page.locator('#g-license').selectOption(APACHE_LICENSE);
155
+ await page.locator('.g-save-item').click();
156
+ await waitForIdlePage(page);
157
+
158
+ await expect(page.locator('.g-item-license')).toContainText(APACHE_LICENSE);
159
+ });
160
+
161
+ test('upload into an item does not show the license widget', async ({ page }) => {
162
+ await login(page, ADMIN.login, ADMIN.password);
163
+ await gotoMyFolders(page);
164
+ await openFolder(page, 'Public');
165
+ await openItem(page, 'Test Item');
166
+
167
+ await expect(page.locator('.g-upload-into-item:visible')).toHaveCount(1);
168
+ await page.locator('.g-upload-into-item:visible').click();
169
+ await expect.poll(() => page.evaluate(() => window.location.hash)).toContain('dialog=upload');
170
+ await waitForDialog(page);
171
+ await expect(page.locator('.g-start-upload:visible')).toContainText('Start Upload');
172
+
173
+ // Uploading into a specific item must not offer a license selection.
174
+ await expect(page.locator('#g-license')).toHaveCount(0);
175
+
176
+ // Close the dialog and return to the folder listing.
177
+ await page.locator('#g-dialog-container .modal-header .close').click();
178
+ await expect(page.locator('#g-dialog-container')).toBeHidden();
179
+ await backToPublicFolder(page);
180
+ });
181
+
182
+ test('upload a file into a folder, specifying a license', async ({ page }) => {
183
+ await login(page, ADMIN.login, ADMIN.password);
184
+ await gotoMyFolders(page);
185
+ await openFolder(page, 'Public');
186
+
187
+ await page.locator('.g-upload-here-button:visible').click();
188
+ await expect.poll(() => page.evaluate(() => window.location.hash)).toContain('dialog=upload');
189
+ await waitForDialog(page);
190
+ await expect(page.locator('.g-start-upload:visible')).toContainText('Start Upload');
191
+
192
+ // Uploading into a folder offers the license selection widget.
193
+ await expectLicenseSelect(page.locator('#g-license'), '');
194
+ await page.locator('#g-license').selectOption(APACHE_LICENSE);
195
+
196
+ await page.locator('#g-files').setInputFiles(path.join(__dirname, 'data', 'testFile.txt'));
197
+ await expect(page.locator('.g-start-upload')).toBeEnabled();
198
+ await page.locator('.g-start-upload').click();
199
+ await waitForIdlePage(page);
200
+ await expect(page.locator('#g-dialog-container')).toBeHidden();
201
+
202
+ await openItem(page, 'testFile.txt');
203
+ await expect(page.locator('.g-item-license')).toContainText(APACHE_LICENSE);
204
+
205
+ await backToPublicFolder(page);
206
+ });
207
+
208
+ test('create an item with an unspecified license', async ({ page }) => {
209
+ await login(page, ADMIN.login, ADMIN.password);
210
+ await gotoMyFolders(page);
211
+ await openFolder(page, 'Public');
212
+ await openCreateItemDialog(page);
213
+
214
+ await expectLicenseSelect(page.locator('#g-license'), '');
215
+
216
+ await page.locator('input#g-name').fill('Test Item 2');
217
+ await page.locator('#g-license').selectOption('');
218
+ await page.locator('.g-save-item').click();
219
+ await waitForIdlePage(page);
220
+
221
+ await openItem(page, 'Test Item 2');
222
+ await expect(page.locator('.g-item-license')).toContainText('Unspecified');
223
+ });
224
+
225
+ test('edit an item that has an unspecified license', async ({ page }) => {
226
+ await login(page, ADMIN.login, ADMIN.password);
227
+ await gotoMyFolders(page);
228
+ await openFolder(page, 'Public');
229
+ await openItem(page, 'Test Item 2');
230
+
231
+ await openEditItemDialog(page);
232
+ // The unspecified license is selected by default.
233
+ await expectLicenseSelect(page.locator('#g-license'), '');
234
+
235
+ await page.locator('#g-license').selectOption(MIT_LICENSE);
236
+ await page.locator('.g-save-item').click();
237
+ await waitForIdlePage(page);
238
+
239
+ await expect(page.locator('.g-item-license')).toContainText(MIT_LICENSE);
240
+ });
241
+
242
+ test('edit an item to have an unspecified license', async ({ page }) => {
243
+ await login(page, ADMIN.login, ADMIN.password);
244
+ await gotoMyFolders(page);
245
+ await openFolder(page, 'Public');
246
+ await openItem(page, 'Test Item 2');
247
+
248
+ await openEditItemDialog(page);
249
+ await expectLicenseSelect(page.locator('#g-license'), MIT_LICENSE);
250
+
251
+ await page.locator('#g-license').selectOption('');
252
+ await page.locator('.g-save-item').click();
253
+ await waitForIdlePage(page);
254
+
255
+ await expect(page.locator('.g-item-license')).toContainText('Unspecified');
256
+ });
257
+ });