python-output-pane.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import React, { FC, PropsWithChildren } from 'react'
  2. import PythonOutputPane from '@/features/ide-react/components/editor/python/python-output-pane'
  3. import {
  4. EditorProviders,
  5. projectDefaults,
  6. } from '../../../helpers/editor-providers'
  7. import { FileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
  8. import { ProjectContext } from '@/shared/context/project-context'
  9. import { ProjectSnapshot } from '@/infrastructure/project-snapshot'
  10. const pythonExecutableScript: Record<string, string> = {
  11. file_id: 'test-py-doc-id',
  12. filename: 'test.py',
  13. }
  14. const FileTreePathProvider: FC<PropsWithChildren> = ({ children }) => {
  15. return (
  16. <FileTreePathContext.Provider
  17. value={{
  18. pathInFolder: () => pythonExecutableScript.filename,
  19. findEntityByPath: () => null,
  20. previewByPath: () => null,
  21. dirname: () => null,
  22. }}
  23. >
  24. {children}
  25. </FileTreePathContext.Provider>
  26. )
  27. }
  28. function makeProjectProvider(fileContents: Record<string, string>) {
  29. const ProjectProvider: FC<PropsWithChildren> = ({ children }) => {
  30. const projectSnapshot = {
  31. refresh: async () => {},
  32. getDocPaths: () => Object.keys(fileContents),
  33. getDocContents: (path: string) => fileContents[path] ?? null,
  34. } as unknown as ProjectSnapshot
  35. return (
  36. <ProjectContext.Provider
  37. value={{
  38. projectId: projectDefaults._id,
  39. project: projectDefaults,
  40. joinProject: () => {},
  41. updateProject: () => {},
  42. joinedOnce: true,
  43. projectSnapshot,
  44. tags: [],
  45. features: projectDefaults.features,
  46. name: projectDefaults.name,
  47. }}
  48. >
  49. {children}
  50. </ProjectContext.Provider>
  51. )
  52. }
  53. return ProjectProvider
  54. }
  55. describe('<PythonOutputPane />', function () {
  56. beforeEach(function () {
  57. window.metaAttributesCache.set('ol-baseAssetPath', '/__cypress/src/')
  58. })
  59. it('executes a Python script and displays its output', function () {
  60. const executablePythonFileContents = "print('hello!')"
  61. const projectFiles = {
  62. [pythonExecutableScript.filename]: executablePythonFileContents,
  63. }
  64. const ProjectProvider = makeProjectProvider(projectFiles)
  65. cy.mount(
  66. <EditorProviders
  67. scope={{
  68. editor: {
  69. sharejs_doc: {
  70. doc_id: pythonExecutableScript.file_id,
  71. getSnapshot: () => executablePythonFileContents,
  72. },
  73. currentDocumentId: pythonExecutableScript.file_id,
  74. openDocName: pythonExecutableScript.filename,
  75. },
  76. }}
  77. providers={{ FileTreePathProvider, ProjectProvider }}
  78. >
  79. <PythonOutputPane />
  80. </EditorProviders>
  81. )
  82. cy.findByRole('button', { name: 'Run Python Code' })
  83. .should('not.be.disabled')
  84. .click()
  85. cy.findByText('hello!').should('exist')
  86. })
  87. it('can import and use values from other project Python files', function () {
  88. const executablePythonFileContents =
  89. 'from message import message\nprint(message)'
  90. const importedPythonFile = {
  91. filename: 'message.py',
  92. file_contents: "message = 'hello!'",
  93. }
  94. const projectFiles = {
  95. [pythonExecutableScript.filename]: executablePythonFileContents,
  96. [importedPythonFile.filename]: importedPythonFile.file_contents,
  97. }
  98. const ProjectProvider = makeProjectProvider(projectFiles)
  99. cy.mount(
  100. <EditorProviders
  101. scope={{
  102. editor: {
  103. sharejs_doc: {
  104. doc_id: pythonExecutableScript.file_id,
  105. getSnapshot: () => executablePythonFileContents,
  106. },
  107. currentDocumentId: pythonExecutableScript.file_id,
  108. openDocName: pythonExecutableScript.filename,
  109. },
  110. }}
  111. providers={{ FileTreePathProvider, ProjectProvider }}
  112. >
  113. <PythonOutputPane />
  114. </EditorProviders>
  115. )
  116. cy.findByRole('button', { name: 'Run Python Code' })
  117. .should('not.be.disabled')
  118. .click()
  119. cy.findByText('hello!').should('exist')
  120. })
  121. it('can import files from different directories relative to the executable script', function () {
  122. const executablePythonFileContents = [
  123. 'from scripts.data_importers.csv_importer import print_data',
  124. 'print_data()',
  125. ].join('\n')
  126. const csvImporterFile = {
  127. filename: 'scripts/data_importers/csv_importer.py',
  128. file_contents: [
  129. 'import csv',
  130. '',
  131. 'def print_data():',
  132. ' with open("food_items.csv", "r") as f:',
  133. ' reader = csv.reader(f)',
  134. ' for row in reader:',
  135. ' print(",".join(row))',
  136. ].join('\n'),
  137. }
  138. const csvDataFile = {
  139. filename: 'food_items.csv',
  140. file_contents: 'name,type\nPizza,Italian\nSushi,Japanese\nTacos,Mexican',
  141. }
  142. const projectFiles = {
  143. 'scripts/test.py': executablePythonFileContents,
  144. [csvImporterFile.filename]: csvImporterFile.file_contents,
  145. [csvDataFile.filename]: csvDataFile.file_contents,
  146. }
  147. const ProjectProvider = makeProjectProvider(projectFiles)
  148. const NestedFileTreePathProvider: FC<PropsWithChildren> = ({
  149. children,
  150. }) => (
  151. <FileTreePathContext.Provider
  152. value={{
  153. pathInFolder: () => 'scripts/test.py',
  154. findEntityByPath: () => null,
  155. previewByPath: () => null,
  156. dirname: () => null,
  157. }}
  158. >
  159. {children}
  160. </FileTreePathContext.Provider>
  161. )
  162. cy.mount(
  163. <EditorProviders
  164. scope={{
  165. editor: {
  166. sharejs_doc: {
  167. doc_id: pythonExecutableScript.file_id,
  168. getSnapshot: () => executablePythonFileContents,
  169. },
  170. currentDocumentId: pythonExecutableScript.file_id,
  171. openDocName: 'test.py',
  172. },
  173. }}
  174. providers={{
  175. FileTreePathProvider: NestedFileTreePathProvider,
  176. ProjectProvider,
  177. }}
  178. >
  179. <PythonOutputPane />
  180. </EditorProviders>
  181. )
  182. cy.findByRole('button', { name: 'Run Python Code' })
  183. .should('not.be.disabled')
  184. .click()
  185. cy.findByText('name,type').should('exist')
  186. cy.findByText('Pizza,Italian').should('exist')
  187. cy.findByText('Sushi,Japanese').should('exist')
  188. cy.findByText('Tacos,Mexican').should('exist')
  189. })
  190. })