import React, { FC, PropsWithChildren } from 'react' import PythonOutputPane from '@/features/ide-react/components/editor/python/python-output-pane' import { EditorProviders, projectDefaults, } from '../../../helpers/editor-providers' import { FileTreePathContext } from '@/features/file-tree/contexts/file-tree-path' import { ProjectContext } from '@/shared/context/project-context' import { ProjectSnapshot } from '@/infrastructure/project-snapshot' import { PythonExecutionProvider } from '@/features/ide-react/context/python-execution-context' const pythonExecutableScript: Record = { file_id: 'test-py-doc-id', filename: 'test.py', } const FileTreePathProvider: FC = ({ children }) => { return ( pythonExecutableScript.filename, findEntityByPath: () => null, previewByPath: () => null, dirname: () => null, }} > {children} ) } function makeProjectProvider(fileContents: Record) { const ProjectProvider: FC = ({ children }) => { const projectSnapshot = { refresh: async () => {}, getDocPaths: () => Object.keys(fileContents), getDocContents: (path: string) => fileContents[path] ?? null, } as unknown as ProjectSnapshot return ( {}, updateProject: () => {}, joinedOnce: true, projectSnapshot, tags: [], features: projectDefaults.features, name: projectDefaults.name, }} > {children} ) } return ProjectProvider } describe('', function () { beforeEach(function () { window.metaAttributesCache.set('ol-baseAssetPath', '/__cypress/src/') }) it('executes a Python script and displays its output', function () { const executablePythonFileContents = "print('hello!')" const projectFiles = { [pythonExecutableScript.filename]: executablePythonFileContents, } const ProjectProvider = makeProjectProvider(projectFiles) cy.mount( executablePythonFileContents, }, currentDocumentId: pythonExecutableScript.file_id, openDocName: pythonExecutableScript.filename, }, }} providers={{ FileTreePathProvider, ProjectProvider }} > ) cy.findByRole('button', { name: 'Run Python code' }) .should('not.be.disabled') .click() cy.findByText('hello!').should('exist') }) it('can import and use values from other project Python files', function () { const executablePythonFileContents = 'from message import message\nprint(message)' const importedPythonFile = { filename: 'message.py', file_contents: "message = 'hello!'", } const projectFiles = { [pythonExecutableScript.filename]: executablePythonFileContents, [importedPythonFile.filename]: importedPythonFile.file_contents, } const ProjectProvider = makeProjectProvider(projectFiles) cy.mount( executablePythonFileContents, }, currentDocumentId: pythonExecutableScript.file_id, openDocName: pythonExecutableScript.filename, }, }} providers={{ FileTreePathProvider, ProjectProvider }} > ) cy.findByRole('button', { name: 'Run Python code' }) .should('not.be.disabled') .click() cy.findByText('hello!').should('exist') }) it('can import files from different directories relative to the executable script', function () { const executablePythonFileContents = [ 'from scripts.data_importers.csv_importer import print_data', 'print_data()', ].join('\n') const csvImporterFile = { filename: 'scripts/data_importers/csv_importer.py', file_contents: [ 'import csv', '', 'def print_data():', ' with open("food_items.csv", "r") as f:', ' reader = csv.reader(f)', ' for row in reader:', ' print(",".join(row))', ].join('\n'), } const csvDataFile = { filename: 'food_items.csv', file_contents: 'name,type\nPizza,Italian\nSushi,Japanese\nTacos,Mexican', } const projectFiles = { 'scripts/test.py': executablePythonFileContents, [csvImporterFile.filename]: csvImporterFile.file_contents, [csvDataFile.filename]: csvDataFile.file_contents, } const ProjectProvider = makeProjectProvider(projectFiles) const NestedFileTreePathProvider: FC = ({ children, }) => ( 'scripts/test.py', findEntityByPath: () => null, previewByPath: () => null, dirname: () => null, }} > {children} ) cy.mount( executablePythonFileContents, }, currentDocumentId: pythonExecutableScript.file_id, openDocName: 'test.py', }, }} providers={{ FileTreePathProvider: NestedFileTreePathProvider, ProjectProvider, }} > ) cy.findByRole('button', { name: 'Run Python code' }) .should('not.be.disabled') .click() cy.findByText('name,type').should('exist') cy.findByText('Pizza,Italian').should('exist') cy.findByText('Sushi,Japanese').should('exist') cy.findByText('Tacos,Mexican').should('exist') }) it('renders stderr output with the stderr line class', function () { const executablePythonFileContents = [ 'import sys', "print('hello!')", "sys.stderr.write('boom\\n')", ].join('\n') const projectFiles = { [pythonExecutableScript.filename]: executablePythonFileContents, } const ProjectProvider = makeProjectProvider(projectFiles) cy.mount( executablePythonFileContents, }, currentDocumentId: pythonExecutableScript.file_id, openDocName: pythonExecutableScript.filename, }, }} providers={{ FileTreePathProvider, ProjectProvider }} > ) cy.findByRole('button', { name: 'Run Python code' }) .should('not.be.disabled') .click() cy.findByText('hello!') .should('have.class', 'ide-redesign-python-output-pane-line-stdout') .and('not.have.class', 'ide-redesign-python-output-pane-line-stderr') cy.findByText('boom').should( 'have.class', 'ide-redesign-python-output-pane-line-stderr' ) }) it('renders the interrupt message as an info line', function () { const executablePythonFileContents = 'while True:\n pass\n' const projectFiles = { [pythonExecutableScript.filename]: executablePythonFileContents, } const ProjectProvider = makeProjectProvider(projectFiles) cy.mount( executablePythonFileContents, }, currentDocumentId: pythonExecutableScript.file_id, openDocName: pythonExecutableScript.filename, }, }} providers={{ FileTreePathProvider, ProjectProvider }} > ) cy.findByRole('button', { name: 'Run Python code' }) .should('not.be.disabled') .click() cy.findByRole('button', { name: 'Stop Python execution' }) .should('not.be.disabled') .click() cy.findByText('Execution interrupted').should( 'have.class', 'ide-redesign-python-output-pane-line-info' ) }) })