From 685943572c6083904baf0d439b88f0a22d42801f Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 20 Mar 2022 15:01:46 +0100 Subject: [PATCH 01/16] Improve save project button --- .../NewProjectModal.tsx | 0 src/components/projects/ProjectHeader.tsx | 24 ++++++++++++++++++ src/context/ProjectProvider.tsx | 20 ++++++++++++++- src/pages/Home.tsx | 25 ++++++++++--------- src/pages/SelectProject.tsx | 2 +- 5 files changed, 57 insertions(+), 14 deletions(-) rename src/components/{select-project => projects}/NewProjectModal.tsx (100%) create mode 100644 src/components/projects/ProjectHeader.tsx diff --git a/src/components/select-project/NewProjectModal.tsx b/src/components/projects/NewProjectModal.tsx similarity index 100% rename from src/components/select-project/NewProjectModal.tsx rename to src/components/projects/NewProjectModal.tsx diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx new file mode 100644 index 0000000..0a35b42 --- /dev/null +++ b/src/components/projects/ProjectHeader.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { IconButton } from 'react-native-paper' +import { useProject } from '../../context/ProjectProvider' + +interface ProjectHeaderProp { + pushMessage: (tag: string) => void +} + +export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { + const { + changed, + actions: { save }, + } = useProject() + + return ( + <> + save().then(() => pushMessage('saved'))} + /> + + ) +} diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx index 80592ff..4ba778b 100644 --- a/src/context/ProjectProvider.tsx +++ b/src/context/ProjectProvider.tsx @@ -13,6 +13,7 @@ import { Reference, Test, } from 'wollok-ts/dist/model' +import { saveProject } from '../services/persistance.service' import { ParentComponentProp } from '../utils/type-helpers' import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers' import { createContextHook } from './create-context-hook' @@ -22,6 +23,7 @@ export const mainPackageName = 'main' export const ProjectContext = createContext<{ project: Environment name: string + changed: boolean actions: Actions } | null>(null) @@ -31,6 +33,7 @@ type Actions = { rebuildEnvironment: (entity: Entity) => void runTest: (test: Test) => TestRun execution: (test: Test) => ExecutionDirector + save: () => Promise } export function ProjectProvider( @@ -42,6 +45,7 @@ export function ProjectProvider( const [project, setProject] = useState( link(props.initialProject.members), ) + const [changed, setChanged] = useState(false) function buildEnvironment( name: Name, @@ -73,6 +77,7 @@ export function ProjectProvider( const packageName = entity.is('Describe') ? 'tests' : mainPackageName setProject(buildEnvironment(packageName, [entity])) //TODO: Run validations + setChanged(true) } function runTest(test: Test) { @@ -83,10 +88,23 @@ export function ProjectProvider( return executionFor(test, project) } + async function save() { + await saveProject(props.projectName, project) + setChanged(false) + } + const initialContext = { project, name: props.projectName, - actions: { addEntity, addDescribe, rebuildEnvironment, runTest, execution }, + changed, + actions: { + addEntity, + addDescribe, + rebuildEnvironment, + runTest, + execution, + save, + }, } return ( diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 6e946e6..a61fbb3 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -4,8 +4,8 @@ import { StackNavigationProp } from '@react-navigation/stack' import React, { useState } from 'react' import { IconButton, Snackbar } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' +import { ProjectHeader } from '../components/projects/ProjectHeader' import { useProject } from '../context/ProjectProvider' -import { saveProject } from '../services/persistance.service' import { wTranslate } from '../utils/translation-helpers' import { Describes } from './Describes' import { Entities } from './Entities/Entities' @@ -20,20 +20,21 @@ const Tab = createBottomTabNavigator() export function Home() { const { name, project } = useProject() - const [saved, setSaved] = useState(false) + // MOve to another component + const [message, setMessage] = useState('') + const [showMessage, setShowMessage] = useState(false) - const navigation = useNavigation() + function pushMessage(tag: string) { + setMessage(tag) + setShowMessage(true) + } + const navigation = useNavigation() React.useLayoutEffect(() => { navigation.setOptions({ title: name, headerTitleAlign: 'center', - headerRight: () => ( - saveProject(name, project).then(() => setSaved(true))} - /> - ), + headerRight: () => , }) }, [navigation, project, name]) @@ -52,13 +53,13 @@ export function Home() { /> { - setSaved(false) + setShowMessage(false) }} duration={2000} wrapperStyle={{ marginBottom: '20%' }}> - {wTranslate('project.saved')} + {wTranslate(`project.${message}`)} ) diff --git a/src/pages/SelectProject.tsx b/src/pages/SelectProject.tsx index 10d3cf7..d3022d8 100644 --- a/src/pages/SelectProject.tsx +++ b/src/pages/SelectProject.tsx @@ -5,7 +5,7 @@ import { List } from 'react-native-paper' import { Environment } from 'wollok-ts/dist/model' import { stylesheet } from '../components/entities/Entity/styles' import FabAddScreen from '../components/FabScreens/FabAddScreen' -import { NewProjectModal } from '../components/select-project/NewProjectModal' +import { NewProjectModal } from '../components/projects/NewProjectModal' import { templateProject } from '../context/initialProject' import { loadProject, From 8360f072984f96a9ccbe00802e05806c0be5a963 Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 20 Mar 2022 18:04:52 +0100 Subject: [PATCH 02/16] Problems view with navigation (using Wollok-TS 3.0.8) --- package.json | 2 +- src/components/projects/ProjectHeader.tsx | 89 ++++++++++++++++++++++- src/context/ProjectProvider.tsx | 10 ++- src/context/initialProject.ts | 2 +- src/services/persistance.service.ts | 3 +- src/utils/wollok-helpers.ts | 6 +- yarn.lock | 14 ++-- 7 files changed, 107 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 0b2cf53..1e241c6 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "react-native-vector-icons": "^8.1.0", "rn-fetch-blob-v2": "^0.14.1", "upper-case-first": "^2.0.2", - "wollok-ts": "^3.0.6" + "wollok-ts": "^3.0.8" }, "devDependencies": { "@babel/core": "^7.12.9", diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 0a35b42..1e89268 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -1,24 +1,105 @@ -import React from 'react' -import { IconButton } from 'react-native-paper' +import { useNavigation } from '@react-navigation/native' +import React, { useState } from 'react' +import { ScrollView } from 'react-native' +import { Badge, IconButton, List, Text } from 'react-native-paper' +import { Entity, Method, Node } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' +import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail' +import { HomeScreenNavigationProp } from '../../pages/Home' +import { methodFQN } from '../../utils/wollok-helpers' +import FormModal from '../ui/FormModal/FormModal' +import { Row } from '../ui/Row' interface ProjectHeaderProp { pushMessage: (tag: string) => void } export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { + const [showProblems, setShowProblems] = useState(false) const { + project, changed, + problems, actions: { save }, } = useProject() + // Duplicated code + const navigation = useNavigation< + HomeScreenNavigationProp & EntityMemberScreenNavigationProp + >() + const goToEntityDetails = (entity: Entity) => { + navigation.navigate('EntityStack', { + entityFQN: entity.fullyQualifiedName(), + }) + + setShowProblems(false) + } + const goToMethod = (method: Method) => { + navigation.navigate('EntityMemberDetails', { + entityMember: method, + fqn: methodFQN(method), + }) + + setShowProblems(false) + } + + const projectProblems = problems.filter(p => + p.node.ancestors().includes(project.getNodeByFQN('main')), + ) //Missing tests + + const nodeDescription = (n: Node) => + n.match({ + Method: methodFQN, + Singleton: s => s.name, + Field: f => f.name, + }) + const goto = (n: Node): void => + n.match({ + Method: goToMethod, + Singleton: goToEntityDetails, + Field: f => goto(f.parent), + }) + return ( - <> + + { + setShowProblems(true) + }} + /> + {projectProblems.length} save().then(() => pushMessage('saved'))} /> - + setShowProblems(false)}> + + {projectProblems.map(problem => ( + goto(problem.node)} + title={problem.code} + left={() => + problem.level === 'error' ? ( + + ) : ( + + ) + } + description={ + <> + {nodeDescription(problem.node)} + + } + /> + ))} + + + ) } diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx index 4ba778b..557c46d 100644 --- a/src/context/ProjectProvider.tsx +++ b/src/context/ProjectProvider.tsx @@ -10,9 +10,11 @@ import { Module, Name, Package, + Problem, Reference, Test, } from 'wollok-ts/dist/model' +import validate from 'wollok-ts/dist/validator' import { saveProject } from '../services/persistance.service' import { ParentComponentProp } from '../utils/type-helpers' import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers' @@ -24,6 +26,7 @@ export const ProjectContext = createContext<{ project: Environment name: string changed: boolean + problems: Problem[] actions: Actions } | null>(null) @@ -46,6 +49,7 @@ export function ProjectProvider( link(props.initialProject.members), ) const [changed, setChanged] = useState(false) + const [problems, setProblems] = useState([] as Problem[]) function buildEnvironment( name: Name, @@ -75,8 +79,9 @@ export function ProjectProvider( function rebuildEnvironment(entity: Entity) { const packageName = entity.is('Describe') ? 'tests' : mainPackageName - setProject(buildEnvironment(packageName, [entity])) - //TODO: Run validations + const newProject = buildEnvironment(packageName, [entity]) + setProject(newProject) + setProblems(validate(newProject) as Problem[]) setChanged(true) } @@ -97,6 +102,7 @@ export function ProjectProvider( project, name: props.projectName, changed, + problems, actions: { addEntity, addDescribe, diff --git a/src/context/initialProject.ts b/src/context/initialProject.ts index b8f5a98..deb0f86 100644 --- a/src/context/initialProject.ts +++ b/src/context/initialProject.ts @@ -4,7 +4,6 @@ import { Describe, Environment, Field, - fromJSON, Import, Literal, Method, @@ -15,6 +14,7 @@ import { Singleton, Test, } from 'wollok-ts/dist/model' +import { fromJSON } from 'wollok-ts/dist/jsonUtils' import WRE from 'wollok-ts/dist/wre/wre.json' import { mainPackageName } from './ProjectProvider' diff --git a/src/services/persistance.service.ts b/src/services/persistance.service.ts index 370098a..5ae87be 100644 --- a/src/services/persistance.service.ts +++ b/src/services/persistance.service.ts @@ -5,7 +5,8 @@ import { writeFile, } from 'react-native-fs' import RNFetchBlob from 'rn-fetch-blob-v2' -import { Environment, fromJSON } from 'wollok-ts/dist/model' +import { Environment } from 'wollok-ts/dist/model' +import { fromJSON } from 'wollok-ts/dist/jsonUtils' import { projectToJSON } from '../utils/wollok-helpers' const projectsFolder = 'projects' diff --git a/src/utils/wollok-helpers.ts b/src/utils/wollok-helpers.ts index a7f3724..499c6c3 100644 --- a/src/utils/wollok-helpers.ts +++ b/src/utils/wollok-helpers.ts @@ -13,7 +13,6 @@ import { Environment, Field, is, - List, Literal, Method, Module, @@ -24,6 +23,7 @@ import { Test, Variable, } from 'wollok-ts/dist/model' +import { List } from 'wollok-ts/dist/extensions' import WRENatives from 'wollok-ts/dist/wre/wre.natives' import { last } from './commons' @@ -66,7 +66,7 @@ export function literalClassFQN(literal: Literal): Name { export function allScopedVariables( node: EntityMemberWithBody, ): Referenciable[] { - const fields = allFields(node.parent()) + const fields = allFields(node.parent) const params = node.is('Method') ? node.parameters : [] const methodVars = allVariables(node) @@ -74,7 +74,7 @@ export function allScopedVariables( } export function methodFQN(method: Method) { - return `${method.parent().fullyQualifiedName()}.${method.name}/${ + return `${method.parent.fullyQualifiedName()}.${method.name}/${ method.parameters.length }` } diff --git a/yarn.lock b/yarn.lock index f0d4423..5b9098f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9544,9 +9544,9 @@ unpipe@~1.0.0: resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unraw@^2.0.0: +unraw@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/unraw/-/unraw-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/unraw/-/unraw-2.0.1.tgz#7b51dcdfb1e43d59d5e52cdb44d349d029edbaba" integrity sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ== unset-value@^1.0.0: @@ -9829,14 +9829,14 @@ widest-line@^2.0.0: dependencies: string-width "^2.1.1" -wollok-ts@^3.0.6: - version "3.0.6" - resolved "https://registry.npmjs.org/wollok-ts/-/wollok-ts-3.0.6.tgz" - integrity sha512-2TgwRhu0+lUgCxULz4ZjMlw8zfv1gKFE7v6eh4fNtVWBdlKozqS6QT3YwNPzbgPolTXW9xrMaAf1lxpsD3ye1Q== +wollok-ts@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/wollok-ts/-/wollok-ts-3.0.8.tgz#dd4bcf5ec2fa323a565e8f8688291b3a61f3765e" + integrity sha512-oBVEPL+MwCVsfoBnN3bLvYsr9vM9OmPdReNQq+sYM0h/l2cn2iYfD0xQdVC9u0hhrqpzfk+es63fUKAVLEkLJQ== dependencies: "@types/parsimmon" "^1.10.6" parsimmon "^1.18.0" - unraw "^2.0.0" + unraw "^2.0.1" uuid "8.3.2" word-wrap@^1.2.3, word-wrap@~1.2.3: From 83e335e455ecbaaf4d85a555d1d79c2b515f3b7f Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 3 Apr 2022 17:49:45 +0200 Subject: [PATCH 03/16] Error icon on attribute item (duplicated code) --- .../entity-detail/AccordionList.tsx | 2 +- .../AttributeItem/AttributeItem.tsx | 19 ++++++++++++++++--- src/components/projects/ProjectHeader.tsx | 8 ++------ src/pages/EntityDetails/EntityDetails.tsx | 11 ++++++++++- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/components/entity-detail/AccordionList.tsx b/src/components/entity-detail/AccordionList.tsx index eb86ad3..8fdceba 100644 --- a/src/components/entity-detail/AccordionList.tsx +++ b/src/components/entity-detail/AccordionList.tsx @@ -2,7 +2,7 @@ import { Theme, useTheme } from '@react-navigation/native' import React, { Key, useState } from 'react' import { StyleSheet, View } from 'react-native' import { Divider, List as ListComponent } from 'react-native-paper' -import { List } from 'wollok-ts/dist/model' +import { List } from 'wollok-ts/dist/extensions' type Props = { title: string diff --git a/src/components/entity-detail/AttributeItem/AttributeItem.tsx b/src/components/entity-detail/AttributeItem/AttributeItem.tsx index 9b8d6d6..86a574d 100644 --- a/src/components/entity-detail/AttributeItem/AttributeItem.tsx +++ b/src/components/entity-detail/AttributeItem/AttributeItem.tsx @@ -1,14 +1,19 @@ import React from 'react' -import { List, withTheme } from 'react-native-paper' -import { Field } from 'wollok-ts/dist/model' +import { IconButton, List, withTheme } from 'react-native-paper' +import { Field, Problem } from 'wollok-ts/dist/model' import { Theme } from '../../../theme' import { ExpressionDisplay } from '../../expressions/ExpressionDisplay' import { ATTRIBUTE_ICONS } from '../attribute-icons' import styles from './styles' -function AttributeItem(props: { attribute: Field; theme: Theme }) { +function AttributeItem(props: { + attribute: Field + problem?: Problem + theme: Theme +}) { const { attribute: { isProperty, isConstant, name, value }, + problem, theme, } = props @@ -26,6 +31,14 @@ function AttributeItem(props: { attribute: Field; theme: Theme }) { return ( + problem && + (problem.level === 'error' ? ( + + ) : ( + + )) + } description={() => value && } right={() => icons diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 1e89268..137a018 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -1,7 +1,7 @@ import { useNavigation } from '@react-navigation/native' import React, { useState } from 'react' import { ScrollView } from 'react-native' -import { Badge, IconButton, List, Text } from 'react-native-paper' +import { Badge, IconButton, List } from 'react-native-paper' import { Entity, Method, Node } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail' @@ -91,11 +91,7 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { ) } - description={ - <> - {nodeDescription(problem.node)} - - } + description={nodeDescription(problem.node)} /> ))} diff --git a/src/pages/EntityDetails/EntityDetails.tsx b/src/pages/EntityDetails/EntityDetails.tsx index a7c1e1e..5cf640b 100644 --- a/src/pages/EntityDetails/EntityDetails.tsx +++ b/src/pages/EntityDetails/EntityDetails.tsx @@ -10,6 +10,7 @@ import NewAttributeModal from '../../components/entity-detail/new-attribute-moda import NewMethodModal from '../../components/entity-detail/new-method-modal/NewMethodModal' import MultiFabScreen from '../../components/FabScreens/MultiFabScreen' import { useEntity } from '../../context/EntityProvider' +import { useProject } from '../../context/ProjectProvider' import { wTranslate } from '../../utils/translation-helpers' import { methodFQN, methodLabel } from '../../utils/wollok-helpers' import { EntityMemberScreenNavigationProp } from '../EntityMemberDetail' @@ -62,7 +63,15 @@ export const EntityDetails = function () { } function AttributeItem({ item: attribute }: { item: Field }) { - return + const { problems } = useProject() + const problem = problems.find(p => p.node.id === attribute.id) + return ( + + ) } function MethodItem({ item: method }: { item: Method }) { From ff53be79c0ec5277e64ac1f00dcc770006170933 Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 7 Apr 2022 23:53:55 +0200 Subject: [PATCH 04/16] Some translations for validations --- src/components/projects/ProjectHeader.tsx | 4 +++- translations/en.json | 5 +++++ translations/es.json | 5 +++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 137a018..7b947f7 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -6,6 +6,7 @@ import { Entity, Method, Node } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail' import { HomeScreenNavigationProp } from '../../pages/Home' +import { wTranslate } from '../../utils/translation-helpers' import { methodFQN } from '../../utils/wollok-helpers' import FormModal from '../ui/FormModal/FormModal' import { Row } from '../ui/Row' @@ -83,7 +84,8 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { {projectProblems.map(problem => ( goto(problem.node)} - title={problem.code} + title={wTranslate(`problem.${problem.code}`)} + titleNumberOfLines={2} left={() => problem.level === 'error' ? ( diff --git a/translations/en.json b/translations/en.json index 19c03a0..f1d2ee8 100644 --- a/translations/en.json +++ b/translations/en.json @@ -65,5 +65,10 @@ "selectProject": "Select a project", "newProject": "New project", "saved": "Saved!" + }, + "problem": { + "shouldInitializeAllAttributes": "All object attributes should be initialized", + "nameShouldBeginWithLowercase": "This name should start with lowercase", + "shouldNotDefineUnusedVariables": "This variable is never used" } } diff --git a/translations/es.json b/translations/es.json index 23b40fb..64cf24e 100644 --- a/translations/es.json +++ b/translations/es.json @@ -65,5 +65,10 @@ "selectProject": "Seleccioná un proyecto", "newProject": "Proyecto nuevo", "saved": "¡Guardado!" + }, + "problem": { + "shouldInitializeAllAttributes": "Todos los atributos del objeto deben estar inicializados", + "nameShouldBeginWithLowercase": "Este nombre debería empezar con minúscula", + "shouldNotDefineUnusedVariables": "Esta variable no se está usando" } } From 312cbc3ee0839c5b8052b9fa4290f637534c91e1 Mon Sep 17 00:00:00 2001 From: palumbon Date: Tue, 12 Apr 2022 21:47:19 +0200 Subject: [PATCH 05/16] Problem components --- src/components/entities/Entity/Entity.tsx | 16 ++++--- .../AttributeItem/AttributeItem.tsx | 28 ++++------- src/components/problems/ProblemIcon.tsx | 16 +++++++ .../problems/ProblemReporterButton.tsx | 41 ++++++++++++++++ src/components/problems/ProblemsModal.tsx | 46 ++++++++++++++++++ src/components/projects/ProjectHeader.tsx | 47 +++++-------------- src/components/ui/FormModal/FormModal.tsx | 22 ++++----- 7 files changed, 143 insertions(+), 73 deletions(-) create mode 100644 src/components/problems/ProblemIcon.tsx create mode 100644 src/components/problems/ProblemReporterButton.tsx create mode 100644 src/components/problems/ProblemsModal.tsx diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx index 01e9b87..38f67af 100644 --- a/src/components/entities/Entity/Entity.tsx +++ b/src/components/entities/Entity/Entity.tsx @@ -4,31 +4,33 @@ import { List, withTheme } from 'react-native-paper' import { Module } from 'wollok-ts/dist/model' import { HomeScreenNavigationProp } from '../../../pages/Home' import { Theme } from '../../../theme' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' import { EntityKindIcon } from '../EntityKindIcon' import { stylesheet } from './styles' -type Props = { +type EntityComponentProps = { entity: Module theme: Theme } -function EntityComponent(props: Props) { - const styles = stylesheet(props.theme) +function EntityComponent({ entity, theme }: EntityComponentProps) { + const styles = stylesheet(theme) const navigation = useNavigation() const goToEntityDetails = () => { navigation.navigate('EntityStack', { - entityFQN: props.entity.fullyQualifiedName(), + entityFQN: entity.fullyQualifiedName(), }) } return ( } + title={entity.name} + left={() => } + right={() => } /> ) } diff --git a/src/components/entity-detail/AttributeItem/AttributeItem.tsx b/src/components/entity-detail/AttributeItem/AttributeItem.tsx index 86a574d..8c85481 100644 --- a/src/components/entity-detail/AttributeItem/AttributeItem.tsx +++ b/src/components/entity-detail/AttributeItem/AttributeItem.tsx @@ -1,21 +1,16 @@ import React from 'react' -import { IconButton, List, withTheme } from 'react-native-paper' -import { Field, Problem } from 'wollok-ts/dist/model' +import { List, withTheme } from 'react-native-paper' +import { Field } from 'wollok-ts/dist/model' import { Theme } from '../../../theme' import { ExpressionDisplay } from '../../expressions/ExpressionDisplay' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' import { ATTRIBUTE_ICONS } from '../attribute-icons' import styles from './styles' -function AttributeItem(props: { - attribute: Field - problem?: Problem - theme: Theme -}) { - const { - attribute: { isProperty, isConstant, name, value }, - problem, - theme, - } = props +function AttributeItem(props: { attribute: Field; theme: Theme }) { + const { attribute, theme } = props + + const { isProperty, isConstant, name, value } = attribute const icons = [ { @@ -31,14 +26,7 @@ function AttributeItem(props: { return ( - problem && - (problem.level === 'error' ? ( - - ) : ( - - )) - } + left={() => } description={() => value && } right={() => icons diff --git a/src/components/problems/ProblemIcon.tsx b/src/components/problems/ProblemIcon.tsx new file mode 100644 index 0000000..d0d0995 --- /dev/null +++ b/src/components/problems/ProblemIcon.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { IconButton } from 'react-native-paper' +import { Problem } from 'wollok-ts' + +interface ProblemIconProps { + problem: Problem + onPress?: () => void +} + +export function ProblemIcon({ problem, onPress }: ProblemIconProps) { + return problem.level === 'error' ? ( + + ) : ( + + ) +} diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx new file mode 100644 index 0000000..2dd6bb6 --- /dev/null +++ b/src/components/problems/ProblemReporterButton.tsx @@ -0,0 +1,41 @@ +import React, { useState } from 'react' +import { Node } from 'wollok-ts' +import { useProject } from '../../context/ProjectProvider' +import { ProblemIcon } from './ProblemIcon' +import { ProblemModal } from './ProblemsModal' + +interface ProblemReporterButtonProps { + node: Node + icon?: JSX.Element +} + +export function ProblemReporterButton({ + node, + icon, +}: ProblemReporterButtonProps) { + const [showProblems, setShowProblems] = useState(false) + const { problems } = useProject() + + const nodeProblems = problems.filter(p => p.node.id === node.id) + + if (!nodeProblems.length) { + return null + } + + return ( + <> + {icon || ( + setShowProblems(true)} + /> + )} + + + + ) +} diff --git a/src/components/problems/ProblemsModal.tsx b/src/components/problems/ProblemsModal.tsx new file mode 100644 index 0000000..3f43742 --- /dev/null +++ b/src/components/problems/ProblemsModal.tsx @@ -0,0 +1,46 @@ +import React from 'react' +import { ScrollView } from 'react-native' +import { List } from 'react-native-paper' +import { Node, Problem } from 'wollok-ts' +import { wTranslate } from '../../utils/translation-helpers' +import { methodFQN } from '../../utils/wollok-helpers' +import FormModal, { FormModalProps } from '../ui/FormModal/FormModal' +import { ProblemIcon } from './ProblemIcon' + +interface ProblemsModalProp { + problems: Problem[] + onSelect?: (p: Problem) => void +} + +export function ProblemModal({ + problems, + onSelect, + visible, + setVisible, +}: ProblemsModalProp & Pick) { + const nodeDescription = (n: Node) => + n.match({ + Method: methodFQN, + Singleton: s => s.name, + Field: f => f.name, + }) + + return ( + setVisible(false)}> + + {problems.map(problem => ( + onSelect && onSelect(problem)} + title={wTranslate(`problem.${problem.code}`)} + titleNumberOfLines={2} + left={() => } + description={nodeDescription(problem.node)} + /> + ))} + + + ) +} diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 7b947f7..d0a65f8 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -1,14 +1,12 @@ import { useNavigation } from '@react-navigation/native' import React, { useState } from 'react' -import { ScrollView } from 'react-native' -import { Badge, IconButton, List } from 'react-native-paper' -import { Entity, Method, Node } from 'wollok-ts' +import { Badge, IconButton } from 'react-native-paper' +import { Entity, Method, Problem } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail' import { HomeScreenNavigationProp } from '../../pages/Home' -import { wTranslate } from '../../utils/translation-helpers' import { methodFQN } from '../../utils/wollok-helpers' -import FormModal from '../ui/FormModal/FormModal' +import { ProblemModal } from '../problems/ProblemsModal' import { Row } from '../ui/Row' interface ProjectHeaderProp { @@ -46,19 +44,13 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { const projectProblems = problems.filter(p => p.node.ancestors().includes(project.getNodeByFQN('main')), - ) //Missing tests + ) //TODO: Missing tests - const nodeDescription = (n: Node) => - n.match({ - Method: methodFQN, - Singleton: s => s.name, - Field: f => f.name, - }) - const goto = (n: Node): void => - n.match({ + const goto = (p: Problem): void => + p.node.match({ Method: goToMethod, Singleton: goToEntityDetails, - Field: f => goto(f.parent), + Field: f => goToEntityDetails(f.parent), }) return ( @@ -76,28 +68,13 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { icon="content-save" onPress={() => save().then(() => pushMessage('saved'))} /> - setShowProblems(false)}> - - {projectProblems.map(problem => ( - goto(problem.node)} - title={wTranslate(`problem.${problem.code}`)} - titleNumberOfLines={2} - left={() => - problem.level === 'error' ? ( - - ) : ( - - ) - } - description={nodeDescription(problem.node)} - /> - ))} - - + onSelect={goto} + /> ) } diff --git a/src/components/ui/FormModal/FormModal.tsx b/src/components/ui/FormModal/FormModal.tsx index 9775eb7..52e24c0 100644 --- a/src/components/ui/FormModal/FormModal.tsx +++ b/src/components/ui/FormModal/FormModal.tsx @@ -12,17 +12,17 @@ import { wTranslate } from '../../../utils/translation-helpers' import { ParentComponentProp } from '../../../utils/type-helpers' import { stylesheet } from './styles' -function FormModal( - props: ParentComponentProp<{ - visible: boolean - setVisible: (value: boolean) => void - onSubmit: () => void - resetForm?: () => void - title?: string - valid?: boolean - theme: Theme - }>, -) { +export type FormModalProps = ParentComponentProp<{ + visible: boolean + setVisible: (value: boolean) => void + onSubmit: () => void + resetForm?: () => void + title?: string + valid?: boolean + theme: Theme +}> + +function FormModal(props: FormModalProps) { const styles = stylesheet(props.theme) const disabledSubmit = props.valid === undefined ? false : !props.valid From bb79c1d3b5d135845d9d0021f3404ba210ee6c8b Mon Sep 17 00:00:00 2001 From: palumbon Date: Tue, 12 Apr 2022 22:08:24 +0200 Subject: [PATCH 06/16] Problems on methods --- src/components/problems/ProblemReporterButton.tsx | 13 +++++++++++-- src/components/problems/ProblemsModal.tsx | 5 ++++- src/components/ui/Body/BodyMaker.tsx | 5 ++++- src/pages/EntityDetails/EntityDetails.tsx | 13 +++---------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx index 2dd6bb6..0f71dc5 100644 --- a/src/components/problems/ProblemReporterButton.tsx +++ b/src/components/problems/ProblemReporterButton.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react' -import { Node } from 'wollok-ts' +import { Node, Problem } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' +import { log } from '../../utils/commons' import { ProblemIcon } from './ProblemIcon' import { ProblemModal } from './ProblemsModal' @@ -16,7 +17,15 @@ export function ProblemReporterButton({ const [showProblems, setShowProblems] = useState(false) const { problems } = useProject() - const nodeProblems = problems.filter(p => p.node.id === node.id) + const belongsTo = (problem: Problem): boolean => + node.match({ + Method: m => problem.node.ancestors().includes(m), + Node: n => n.id === problem.node.id, + }) + + log(problems[problems.length - 1].node.ancestors().map(a => a.kind)) + + const nodeProblems = problems.filter(belongsTo) if (!nodeProblems.length) { return null diff --git a/src/components/problems/ProblemsModal.tsx b/src/components/problems/ProblemsModal.tsx index 3f43742..c109a92 100644 --- a/src/components/problems/ProblemsModal.tsx +++ b/src/components/problems/ProblemsModal.tsx @@ -18,11 +18,14 @@ export function ProblemModal({ visible, setVisible, }: ProblemsModalProp & Pick) { - const nodeDescription = (n: Node) => + const nodeDescription = (n: Node): string | undefined => n.match({ Method: methodFQN, Singleton: s => s.name, Field: f => f.name, + Assignment: a => nodeDescription(a.parent), + Body: b => nodeDescription(b.parent), + Expression: _e => 'HOLA', }) return ( diff --git a/src/components/ui/Body/BodyMaker.tsx b/src/components/ui/Body/BodyMaker.tsx index 61f412f..fabf828 100644 --- a/src/components/ui/Body/BodyMaker.tsx +++ b/src/components/ui/Body/BodyMaker.tsx @@ -3,12 +3,14 @@ import React, { useState } from 'react' import { ScrollView, StyleSheet, Text } from 'react-native' import { IconButton } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' -import { Body, List, Name, Sentence } from 'wollok-ts/dist/model' +import { List } from 'wollok-ts/dist/extensions' +import { Body, Name, Sentence } from 'wollok-ts/dist/model' import { wTranslate } from '../../../utils/translation-helpers' import { Referenciable } from '../../../utils/wollok-helpers' import { ReferenceSegment } from '../../expressions/expression-segment' import { display, ExpressionDisplay } from '../../expressions/ExpressionDisplay' import MultiFabScreen from '../../FabScreens/MultiFabScreen' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' import { SubmitCheckButton } from '../Header' import { Row } from '../Row' import { AssignmentFormModal } from './AssignmentFormModal' @@ -84,6 +86,7 @@ export function BodyMaker({ case 'Assignment': return ( + p.node.id === attribute.id) - return ( - - ) + return } function MethodItem({ item: method }: { item: Method }) { @@ -80,6 +72,7 @@ function MethodItem({ item: method }: { item: Method }) { } onPress={() => navigator.navigate('EntityMemberDetails', { entityMember: method, From c903a599fe8d244ba9f29b2fb736e844f55cb834 Mon Sep 17 00:00:00 2001 From: palumbon Date: Tue, 12 Apr 2022 22:25:09 +0200 Subject: [PATCH 07/16] Problems in tests --- src/components/problems/ProblemReporterButton.tsx | 4 +--- src/components/problems/ProblemsModal.tsx | 3 ++- src/components/projects/ProjectHeader.tsx | 14 ++++++++------ src/components/tests/TestItem.tsx | 8 +++++++- src/components/ui/Body/BodyMaker.tsx | 13 ++++++++----- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx index 0f71dc5..963ac38 100644 --- a/src/components/problems/ProblemReporterButton.tsx +++ b/src/components/problems/ProblemReporterButton.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react' import { Node, Problem } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' -import { log } from '../../utils/commons' import { ProblemIcon } from './ProblemIcon' import { ProblemModal } from './ProblemsModal' @@ -20,11 +19,10 @@ export function ProblemReporterButton({ const belongsTo = (problem: Problem): boolean => node.match({ Method: m => problem.node.ancestors().includes(m), + Test: t => problem.node.ancestors().includes(t), Node: n => n.id === problem.node.id, }) - log(problems[problems.length - 1].node.ancestors().map(a => a.kind)) - const nodeProblems = problems.filter(belongsTo) if (!nodeProblems.length) { diff --git a/src/components/problems/ProblemsModal.tsx b/src/components/problems/ProblemsModal.tsx index c109a92..25a7b1f 100644 --- a/src/components/problems/ProblemsModal.tsx +++ b/src/components/problems/ProblemsModal.tsx @@ -25,7 +25,8 @@ export function ProblemModal({ Field: f => f.name, Assignment: a => nodeDescription(a.parent), Body: b => nodeDescription(b.parent), - Expression: _e => 'HOLA', + Expression: e => nodeDescription(e.parent), + Test: t => t.fullyQualifiedName(), }) return ( diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index d0a65f8..1c31e1d 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -1,7 +1,7 @@ import { useNavigation } from '@react-navigation/native' import React, { useState } from 'react' import { Badge, IconButton } from 'react-native-paper' -import { Entity, Method, Problem } from 'wollok-ts' +import { Entity, Method, Node } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' import { EntityMemberScreenNavigationProp } from '../../pages/EntityMemberDetail' import { HomeScreenNavigationProp } from '../../pages/Home' @@ -42,12 +42,14 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { setShowProblems(false) } - const projectProblems = problems.filter(p => - p.node.ancestors().includes(project.getNodeByFQN('main')), + const projectProblems = problems.filter( + p => + p.node.ancestors().includes(project.getNodeByFQN('main')) || + p.node.ancestors().includes(project.getNodeByFQN('tests')), ) //TODO: Missing tests - const goto = (p: Problem): void => - p.node.match({ + const goto = (n: Node): void => + n.match({ Method: goToMethod, Singleton: goToEntityDetails, Field: f => goToEntityDetails(f.parent), @@ -73,7 +75,7 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { problems={projectProblems} visible={showProblems} setVisible={setShowProblems} - onSelect={goto} + onSelect={p => goto(p.node)} /> ) diff --git a/src/components/tests/TestItem.tsx b/src/components/tests/TestItem.tsx index 257c73d..9c57086 100644 --- a/src/components/tests/TestItem.tsx +++ b/src/components/tests/TestItem.tsx @@ -13,6 +13,7 @@ import { Theme } from '../../theme' import { runAsync } from '../../utils/commons' import { Maybe } from '../../utils/type-helpers' import { TestRun } from '../../utils/wollok-helpers' +import { ProblemReporterButton } from '../problems/ProblemReporterButton' import FormModal from '../ui/FormModal/FormModal' type TestItemProps = { @@ -30,7 +31,12 @@ function TestItem({ item: test, runner, onClick, theme }: TestItemProps) { <> } + left={() => ( + <> + + + + )} right={() => ( <> {testRun?.exception?.message && ( diff --git a/src/components/ui/Body/BodyMaker.tsx b/src/components/ui/Body/BodyMaker.tsx index fabf828..18827a5 100644 --- a/src/components/ui/Body/BodyMaker.tsx +++ b/src/components/ui/Body/BodyMaker.tsx @@ -77,11 +77,14 @@ export function BodyMaker({ switch (sentence.kind) { case 'Send': return ( - + + + + ) case 'Assignment': return ( From b521ef5c7a9c9da2d55d8118066fd919ca4c2c7b Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 14 Apr 2022 09:11:36 +0200 Subject: [PATCH 08/16] Fix problem navigation to tests --- src/components/projects/ProjectHeader.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 1c31e1d..eb6d409 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -46,13 +46,18 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { p => p.node.ancestors().includes(project.getNodeByFQN('main')) || p.node.ancestors().includes(project.getNodeByFQN('tests')), - ) //TODO: Missing tests + ) //TODO: Move to project provider const goto = (n: Node): void => n.match({ Method: goToMethod, Singleton: goToEntityDetails, Field: f => goToEntityDetails(f.parent), + Assignment: a => goto(a.parent), + Body: b => goto(b.parent), + Expression: e => goto(e.parent), + Test: t => goto(t.parent), + Describe: t => goToEntityDetails(t), }) return ( From 37af99db59d73a1e79ff8a3ca641a21c44b65f6c Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 14 Apr 2022 09:24:14 +0200 Subject: [PATCH 09/16] Interested this project's problems --- src/components/projects/ProjectHeader.tsx | 13 +++------- src/context/ProjectProvider.tsx | 30 ++++++++++++++++++++--- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index eb6d409..0c44929 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -16,7 +16,6 @@ interface ProjectHeaderProp { export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { const [showProblems, setShowProblems] = useState(false) const { - project, changed, problems, actions: { save }, @@ -42,12 +41,6 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { setShowProblems(false) } - const projectProblems = problems.filter( - p => - p.node.ancestors().includes(project.getNodeByFQN('main')) || - p.node.ancestors().includes(project.getNodeByFQN('tests')), - ) //TODO: Move to project provider - const goto = (n: Node): void => n.match({ Method: goToMethod, @@ -63,13 +56,13 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { return ( { setShowProblems(true) }} /> - {projectProblems.length} + {problems.length} goto(p.node)} diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx index 557c46d..4ca618b 100644 --- a/src/context/ProjectProvider.tsx +++ b/src/context/ProjectProvider.tsx @@ -21,6 +21,7 @@ import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers' import { createContextHook } from './create-context-hook' export const mainPackageName = 'main' +export const testsPackageName = 'tests' export const ProjectContext = createContext<{ project: Environment @@ -49,7 +50,11 @@ export function ProjectProvider( link(props.initialProject.members), ) const [changed, setChanged] = useState(false) - const [problems, setProblems] = useState([] as Problem[]) + const [problems, setProblems] = useState( + validateProject(project) as Problem[], + ) + + /////////////////////////////////// BUILD ////////////////////////////////// function buildEnvironment( name: Name, @@ -78,13 +83,30 @@ export function ProjectProvider( } function rebuildEnvironment(entity: Entity) { - const packageName = entity.is('Describe') ? 'tests' : mainPackageName + const packageName = entity.is('Describe') + ? testsPackageName + : mainPackageName const newProject = buildEnvironment(packageName, [entity]) setProject(newProject) - setProblems(validate(newProject) as Problem[]) + setProblems(validateProject(newProject) as Problem[]) setChanged(true) } + function validateProject(_project: Environment) { + const targetPackages = [ + project.getNodeByFQN(mainPackageName), + project.getNodeByFQN(testsPackageName), + ] + const belongsToTargetProject = (p: Problem) => + targetPackages.some(target => p.node.ancestors().includes(target)) + + return validate(_project).filter(belongsToTargetProject) + } + + /////////////////////////////////// BUILD ////////////////////////////////// + + /////////////////////////////////// EXECUTION ////////////////////////////////// + function runTest(test: Test) { return interpretTest(test, project) } @@ -93,6 +115,8 @@ export function ProjectProvider( return executionFor(test, project) } + /////////////////////////////////// EXECUTION ////////////////////////////////// + async function save() { await saveProject(props.projectName, project) setChanged(false) From 432c0449830dfc30f7ac211f0574f1f1b6702d1b Mon Sep 17 00:00:00 2001 From: palumbon Date: Thu, 14 Apr 2022 10:14:49 +0200 Subject: [PATCH 10/16] Fixing tests --- jest.config.js | 1 + src/__tests__/TestItem-test.tsx | 25 +++++++++++-------- src/__tests__/mocks/TestProjectProvider.tsx | 14 +++++++++++ .../expression-lists/messages-list.tsx | 2 +- src/context/ExpressionContextProvider.tsx | 3 ++- 5 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 src/__tests__/mocks/TestProjectProvider.tsx diff --git a/jest.config.js b/jest.config.js index e76b70d..71d4036 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,6 +12,7 @@ module.exports = { '^.+\\.ts?$': 'ts-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + modulePathIgnorePatterns: ['mocks'], setupFilesAfterEnv: [ "@testing-library/jest-native/extend-expect", "./setup-tests.js" diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem-test.tsx index f6df70d..9d7d51a 100644 --- a/src/__tests__/TestItem-test.tsx +++ b/src/__tests__/TestItem-test.tsx @@ -1,12 +1,13 @@ -import React from 'react' import { fireEvent, render } from '@testing-library/react-native' -import TestItem from '../components/tests/TestItem' -import { Body, Test } from 'wollok-ts/dist/model' -import { TestRun } from '../utils/wollok-helpers' +import React from 'react' import { ActivityIndicator } from 'react-native-paper' import { act, ReactTestInstance } from 'react-test-renderer' -import { theme } from '../theme' import { WollokException } from 'wollok-ts/dist/interpreter/runtimeModel' +import { Body, Test } from 'wollok-ts/dist/model' +import TestItem from '../components/tests/TestItem' +import { theme } from '../theme' +import { TestRun } from '../utils/wollok-helpers' +import TestProjectProvider from './mocks/TestProjectProvider' const testMock = new Test({ name: 'TEST', @@ -48,12 +49,14 @@ const renderTest = (runner: () => TestRun = jest.fn()) => { UNSAFE_queryByProps, queryByText, } = render( - , + + + , ) return { runIcon: () => UNSAFE_getByProps({ icon: 'play-circle' }), diff --git a/src/__tests__/mocks/TestProjectProvider.tsx b/src/__tests__/mocks/TestProjectProvider.tsx new file mode 100644 index 0000000..02625d3 --- /dev/null +++ b/src/__tests__/mocks/TestProjectProvider.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import { templateProject } from '../../context/initialProject' +import { ProjectProvider } from '../../context/ProjectProvider' +import { ParentComponentProp } from '../../utils/type-helpers' + +const TestProjectProvider = ({ children }: ParentComponentProp<{}>) => ( + + {children} + +) + +export default TestProjectProvider diff --git a/src/components/expressions/expression-lists/messages-list.tsx b/src/components/expressions/expression-lists/messages-list.tsx index aed986f..2d1da41 100644 --- a/src/components/expressions/expression-lists/messages-list.tsx +++ b/src/components/expressions/expression-lists/messages-list.tsx @@ -2,12 +2,12 @@ import { useNavigation } from '@react-navigation/core' import React from 'react' import { StyleSheet } from 'react-native' import { List as ListComponent, Text } from 'react-native-paper' +import { List } from 'wollok-ts/dist/extensions' import { Class, Environment, Expression, is, - List, Method, Send, } from 'wollok-ts/dist/model' diff --git a/src/context/ExpressionContextProvider.tsx b/src/context/ExpressionContextProvider.tsx index 19a1d64..fff5446 100644 --- a/src/context/ExpressionContextProvider.tsx +++ b/src/context/ExpressionContextProvider.tsx @@ -1,5 +1,6 @@ import React, { createContext, useState } from 'react' -import { List, Method, Module, Name, Test } from 'wollok-ts/dist/model' +import { List } from 'wollok-ts/dist/extensions' +import { Method, Module, Name, Test } from 'wollok-ts/dist/model' import { ParentComponentProp } from '../utils/type-helpers' import { Named } from '../utils/wollok-helpers' import { createContextHook } from './create-context-hook' From faff0a78fcfaf28775eda3436668793889ab3e8b Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 17 Apr 2022 22:38:53 +0200 Subject: [PATCH 11/16] Testing Problem components --- src/__tests__/Problems-test.tsx | 95 +++++++++++++++ src/__tests__/TestItem-test.tsx | 6 +- src/__tests__/mocks/ProjectProviderMock.tsx | 34 ++++++ src/__tests__/mocks/TestProjectProvider.tsx | 14 --- src/__tests__/mocks/wollokProject.ts | 121 ++++++++++++++++++++ src/components/problems/ProblemsModal.tsx | 12 +- 6 files changed, 259 insertions(+), 23 deletions(-) create mode 100644 src/__tests__/Problems-test.tsx create mode 100644 src/__tests__/mocks/ProjectProviderMock.tsx delete mode 100644 src/__tests__/mocks/TestProjectProvider.tsx create mode 100644 src/__tests__/mocks/wollokProject.ts diff --git a/src/__tests__/Problems-test.tsx b/src/__tests__/Problems-test.tsx new file mode 100644 index 0000000..ea49fa9 --- /dev/null +++ b/src/__tests__/Problems-test.tsx @@ -0,0 +1,95 @@ +import { render } from '@testing-library/react-native' +import React from 'react' +import { Node, Problem } from 'wollok-ts' +import { ProblemIcon } from '../components/problems/ProblemIcon' +import { ProblemReporterButton } from '../components/problems/ProblemReporterButton' +import { ProblemModal } from '../components/problems/ProblemsModal' +import { methodFQN } from '../utils/wollok-helpers' +import ProjectProviderMock from './mocks/ProjectProviderMock' +import { + clazz, + error, + field, + method, + problem, + sentence, + singleton, + test, + warning, +} from './mocks/wollokProject' + +describe('ProblemIcon', () => { + it('is red on error', () => { + const { UNSAFE_getByProps } = render() + expect( + UNSAFE_getByProps({ icon: 'alert-circle', color: 'red' }), + ).toBeDefined() + }) + + it('is yellow on warning', () => { + const { UNSAFE_getByProps } = render() + expect(UNSAFE_getByProps({ icon: 'alert', color: 'yellow' })).toBeDefined() + }) +}) + +describe('ProblemReporterButton', () => { + function renderProblemReporterButton(aNode: Node, ...problems: Problem[]) { + return render( + + + , + ) + } + + it('should not be shown if there is no related problems', () => { + const rendered = renderProblemReporterButton(singleton) + expect(rendered.toJSON()).toBeNull() + }) + + describe('should render if there is any problem', () => { + function iconExistTest(name: string, node: Node, problem: Problem) { + it(name, () => { + const rendered = renderProblemReporterButton(node, problem) + expect(rendered.toJSON()).not.toBeNull() + }) + } + + iconExistTest('with problem node', singleton, warning) + + iconExistTest('inside method body', method, error) + + iconExistTest('inside test body', test, problem(test.sentences()[0])) + }) +}) + +describe('ProblemModal', () => { + function renderProblemModal(...problems: Problem[]) { + return render( + , + ) + } + + describe('should render description for', () => { + function problemDescriptionTest( + name: string, + description: string, + problem: Problem, + ) { + it(name, () => { + const { getByText } = renderProblemModal(problem) + expect(getByText(description)).toBeDefined() + }) + } + + problemDescriptionTest('singleton', singleton.name!, warning) + problemDescriptionTest('class', clazz.name!, problem(clazz)) + problemDescriptionTest('field', field.name, problem(field)) + problemDescriptionTest('method', methodFQN(method), problem(method)) + problemDescriptionTest('sentence', methodFQN(method), problem(sentence)) + }) +}) diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem-test.tsx index 9d7d51a..30aea75 100644 --- a/src/__tests__/TestItem-test.tsx +++ b/src/__tests__/TestItem-test.tsx @@ -7,7 +7,7 @@ import { Body, Test } from 'wollok-ts/dist/model' import TestItem from '../components/tests/TestItem' import { theme } from '../theme' import { TestRun } from '../utils/wollok-helpers' -import TestProjectProvider from './mocks/TestProjectProvider' +import ProjectProviderMock from './mocks/ProjectProviderMock' const testMock = new Test({ name: 'TEST', @@ -49,14 +49,14 @@ const renderTest = (runner: () => TestRun = jest.fn()) => { UNSAFE_queryByProps, queryByText, } = render( - + - , + , ) return { runIcon: () => UNSAFE_getByProps({ icon: 'play-circle' }), diff --git a/src/__tests__/mocks/ProjectProviderMock.tsx b/src/__tests__/mocks/ProjectProviderMock.tsx new file mode 100644 index 0000000..9a340c4 --- /dev/null +++ b/src/__tests__/mocks/ProjectProviderMock.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { Environment, Problem } from 'wollok-ts' +import { ProjectContext } from '../../context/ProjectProvider' +import { ParentComponentProp } from '../../utils/type-helpers' + +export const initialContext = { + project: new Environment({ members: [] }), + name: 'Project Test', + changed: false, + problems: [] as Problem[], + actions: { + addEntity: jest.fn(), + addDescribe: jest.fn(), + rebuildEnvironment: jest.fn(), + runTest: jest.fn(), + execution: jest.fn(), + save: jest.fn(), + }, +} + +type InitialProject = Partial + +const ProjectProviderMock = ({ + children, + ...props +}: ParentComponentProp) => { + return ( + + {children} + + ) +} + +export default ProjectProviderMock diff --git a/src/__tests__/mocks/TestProjectProvider.tsx b/src/__tests__/mocks/TestProjectProvider.tsx deleted file mode 100644 index 02625d3..0000000 --- a/src/__tests__/mocks/TestProjectProvider.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { templateProject } from '../../context/initialProject' -import { ProjectProvider } from '../../context/ProjectProvider' -import { ParentComponentProp } from '../../utils/type-helpers' - -const TestProjectProvider = ({ children }: ParentComponentProp<{}>) => ( - - {children} - -) - -export default TestProjectProvider diff --git a/src/__tests__/mocks/wollokProject.ts b/src/__tests__/mocks/wollokProject.ts new file mode 100644 index 0000000..644e0fb --- /dev/null +++ b/src/__tests__/mocks/wollokProject.ts @@ -0,0 +1,121 @@ +import { + Body, + Class, + Describe, + Environment, + Field, + Import, + link, + Literal, + Method, + Node, + Package, + Parameter, + Problem, + Reference, + Send, + Singleton, + Test, + WRE, +} from 'wollok-ts' +import { fromJSON } from 'wollok-ts/dist/jsonUtils' + +const main = new Package({ + name: 'main', + members: [ + new Singleton({ + name: 'pepita', + members: [ + new Field({ + name: 'energia', + isConstant: false, + isProperty: true, + value: new Literal({ value: 100 }), + }), + new Method({ + name: 'come', + parameters: [ + new Parameter({ + name: 'comida', + }), + ], + body: new Body({ + sentences: [ + new Send({ + receiver: new Reference({ name: 'comida' }), + message: 'energiaQueAporta', + }), + ], + }), + }), + ], + }), + new Class({ + name: 'Entrenador', + }), + ], +}) + +const tests = new Package({ + name: 'tests', + imports: [ + new Import({ + entity: new Reference({ name: 'main' }), + isGeneric: true, + }), + ], + members: [ + new Describe({ + name: 'Main Describe', + members: [ + new Test({ + id: 'TEST', + name: 'test for testing', + body: new Body({ + sentences: [ + new Send({ + receiver: new Reference({ name: 'assert' }), + message: 'that', + args: [new Literal({ value: true })], + }), + ], + }), + }), + ], + }), + ], +}) + +export const project = link([main, tests], fromJSON(WRE)) + +export const singleton = project.getNodeByFQN('main.pepita') as Singleton + +export const clazz = project.getNodeByFQN('main.Entrenador') as Singleton + +export const describe = project.getNodeByFQN('tests.Main Describe') as Describe + +export const field = singleton.members[0] as Field + +export const method = singleton.members[1] as Method + +export const test = describe.members[0] as Test + +export const sentence = method.sentences()[0] + +// PROBLEMS + +export const problem = (node: Node): Problem => ({ + node, + code: 'ERROR', + level: 'error', + values: [], +}) + +export const error = problem(sentence) + +export const warning: Problem = { + node: singleton, + code: 'SINGLETON_WARNING', + level: 'warning', + values: [], +} diff --git a/src/components/problems/ProblemsModal.tsx b/src/components/problems/ProblemsModal.tsx index 25a7b1f..98dce53 100644 --- a/src/components/problems/ProblemsModal.tsx +++ b/src/components/problems/ProblemsModal.tsx @@ -20,13 +20,12 @@ export function ProblemModal({ }: ProblemsModalProp & Pick) { const nodeDescription = (n: Node): string | undefined => n.match({ - Method: methodFQN, - Singleton: s => s.name, + Entity: s => s.name, Field: f => f.name, - Assignment: a => nodeDescription(a.parent), + Method: methodFQN, + Test: t => t.name, Body: b => nodeDescription(b.parent), - Expression: e => nodeDescription(e.parent), - Test: t => t.fullyQualifiedName(), + Sentence: a => nodeDescription(a.parent), }) return ( @@ -35,8 +34,9 @@ export function ProblemModal({ setVisible={setVisible} onSubmit={() => setVisible(false)}> - {problems.map(problem => ( + {problems.map((problem, i) => ( onSelect && onSelect(problem)} title={wTranslate(`problem.${problem.code}`)} titleNumberOfLines={2} From f0995127d9e62f5c271105490aebaac2f4b5fd44 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 18 Apr 2022 13:08:43 +0200 Subject: [PATCH 12/16] Show error > warning --- src/__tests__/Problems-test.tsx | 31 ++++++++++++++----- .../problems/ProblemReporterButton.tsx | 5 ++- src/utils/wollok-helpers.ts | 11 ++++++- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/__tests__/Problems-test.tsx b/src/__tests__/Problems-test.tsx index ea49fa9..34ca60e 100644 --- a/src/__tests__/Problems-test.tsx +++ b/src/__tests__/Problems-test.tsx @@ -1,4 +1,4 @@ -import { render } from '@testing-library/react-native' +import { render, RenderAPI } from '@testing-library/react-native' import React from 'react' import { Node, Problem } from 'wollok-ts' import { ProblemIcon } from '../components/problems/ProblemIcon' @@ -20,15 +20,13 @@ import { describe('ProblemIcon', () => { it('is red on error', () => { - const { UNSAFE_getByProps } = render() - expect( - UNSAFE_getByProps({ icon: 'alert-circle', color: 'red' }), - ).toBeDefined() + const rendered = render() + expectErrorIcon(rendered) }) it('is yellow on warning', () => { - const { UNSAFE_getByProps } = render() - expect(UNSAFE_getByProps({ icon: 'alert', color: 'yellow' })).toBeDefined() + const rendered = render() + expectWarningIcon(rendered) }) }) @@ -46,6 +44,15 @@ describe('ProblemReporterButton', () => { expect(rendered.toJSON()).toBeNull() }) + it('should show error if there is related warnings and problems', () => { + const rendered = renderProblemReporterButton( + singleton, + warning, + problem(singleton), + ) + expectErrorIcon(rendered) + }) + describe('should render if there is any problem', () => { function iconExistTest(name: string, node: Node, problem: Problem) { it(name, () => { @@ -93,3 +100,13 @@ describe('ProblemModal', () => { problemDescriptionTest('sentence', methodFQN(method), problem(sentence)) }) }) + +function expectErrorIcon({ UNSAFE_getByProps }: RenderAPI) { + expect( + UNSAFE_getByProps({ icon: 'alert-circle', color: 'red' }), + ).toBeDefined() +} + +function expectWarningIcon({ UNSAFE_getByProps }: RenderAPI) { + expect(UNSAFE_getByProps({ icon: 'alert', color: 'yellow' })).toBeDefined() +} diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx index 963ac38..4f0d290 100644 --- a/src/components/problems/ProblemReporterButton.tsx +++ b/src/components/problems/ProblemReporterButton.tsx @@ -1,6 +1,7 @@ import React, { useState } from 'react' import { Node, Problem } from 'wollok-ts' import { useProject } from '../../context/ProjectProvider' +import { isError } from '../../utils/wollok-helpers' import { ProblemIcon } from './ProblemIcon' import { ProblemModal } from './ProblemsModal' @@ -29,11 +30,13 @@ export function ProblemReporterButton({ return null } + const maybeError = nodeProblems.find(isError) + return ( <> {icon || ( setShowProblems(true)} /> )} diff --git a/src/utils/wollok-helpers.ts b/src/utils/wollok-helpers.ts index 499c6c3..d479504 100644 --- a/src/utils/wollok-helpers.ts +++ b/src/utils/wollok-helpers.ts @@ -1,6 +1,7 @@ // TODO: import form Wollok // All these funtions are duplicated from Wollok import { upperCaseFirst } from 'upper-case-first' +import { List } from 'wollok-ts/dist/extensions' import interpret, { DirectedInterpreter, ExecutionDirector, @@ -19,11 +20,11 @@ import { Name, Node, Parameter, + Problem, Singleton, Test, Variable, } from 'wollok-ts/dist/model' -import { List } from 'wollok-ts/dist/extensions' import WRENatives from 'wollok-ts/dist/wre/wre.natives' import { last } from './commons' @@ -59,6 +60,10 @@ export function entityMemberLabel(node: EntityMemberWithBody): string { return node.is('Method') ? methodLabel(node) : node.name } +export function entityMemberFQN(node: EntityMemberWithBody): string { + return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName() +} + export function literalClassFQN(literal: Literal): Name { return `wollok.lang.${upperCaseFirst(typeof literal.value)}` } @@ -97,6 +102,10 @@ export function methodByFQN(environment: Environment, fqn: Name): Method { return entity.lookupMethod(methodName, Number.parseInt(methodArity, 10))! } +export function isError(problem: Problem): boolean { + return problem.level === 'error' +} + export type TestResult = 'Passed' | 'Failure' | 'Error' export type TestRun = { result: TestResult; exception?: WollokException } export function interpretTest(test: Test, environment: Environment): TestRun { From 7069538056b9a609a0a94d2dc2ca1d56f80a6344 Mon Sep 17 00:00:00 2001 From: palumbon Date: Mon, 18 Apr 2022 18:20:53 +0200 Subject: [PATCH 13/16] Fix navigation: - Bye inner navigator - Fix problems navigation - Renames --- src/__tests__/Problems-test.tsx | 2 +- src/__tests__/TestItem-test.tsx | 3 +- src/__tests__/mocks/ProjectProviderMock.tsx | 4 +- .../entity-detail/ModuleDetails.tsx} | 49 +++++---- .../new-attribute-modal/NewAttributeModal.tsx | 43 ++++---- .../new-method-modal/NewMethodModal.tsx | 28 +++-- .../LiteralModal/LiteralInputModals.tsx | 2 +- .../expression-lists/messages-list.tsx | 4 +- src/components/problems/ProblemIcon.tsx | 2 +- .../problems/ProblemReporterButton.tsx | 2 +- src/components/problems/ProblemsModal.tsx | 2 +- src/components/projects/ProjectHeader.tsx | 37 +++---- src/components/tests/NewTestModal.tsx | 21 ++-- src/components/tests/Tests.tsx | 22 ++-- .../ui/Body/AssignmentFormModal.tsx | 2 +- src/components/ui/ExpressionInput.tsx | 6 +- src/components/ui/FormModal/FormModal.tsx | 20 ++-- src/context/EntityProvider.tsx | 58 ---------- src/context/ProjectProvider.tsx | 58 +++++++--- ...{NewMessageCall.tsx => ArgumentsMaker.tsx} | 10 +- src/pages/Editor.tsx | 56 ++++++++++ src/pages/EntityMemberDetail.tsx | 46 -------- src/pages/EntityStack.tsx | 103 ++++-------------- .../{ExpressionMaker => }/ExpressionMaker.tsx | 46 ++++---- src/pages/Home.tsx | 6 +- src/pages/ProjectNavigator.tsx | 28 ++++- src/pages/{ => tabs}/Describes.tsx | 8 +- .../Entities.tsx => tabs/Modules.tsx} | 2 +- src/utils/type-helpers.ts | 5 + src/utils/wollok-helpers.ts | 31 ++++-- 30 files changed, 334 insertions(+), 372 deletions(-) rename src/{pages/EntityDetails/EntityDetails.tsx => components/entity-detail/ModuleDetails.tsx} (60%) delete mode 100644 src/context/EntityProvider.tsx rename src/pages/{NewMessageCall.tsx => ArgumentsMaker.tsx} (86%) create mode 100644 src/pages/Editor.tsx delete mode 100644 src/pages/EntityMemberDetail.tsx rename src/pages/{ExpressionMaker => }/ExpressionMaker.tsx (72%) rename src/pages/{ => tabs}/Describes.tsx (73%) rename src/pages/{Entities/Entities.tsx => tabs/Modules.tsx} (97%) diff --git a/src/__tests__/Problems-test.tsx b/src/__tests__/Problems-test.tsx index 34ca60e..5e77c4f 100644 --- a/src/__tests__/Problems-test.tsx +++ b/src/__tests__/Problems-test.tsx @@ -1,6 +1,6 @@ import { render, RenderAPI } from '@testing-library/react-native' import React from 'react' -import { Node, Problem } from 'wollok-ts' +import { Node, Problem } from 'wollok-ts/dist/model' import { ProblemIcon } from '../components/problems/ProblemIcon' import { ProblemReporterButton } from '../components/problems/ProblemReporterButton' import { ProblemModal } from '../components/problems/ProblemsModal' diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem-test.tsx index 30aea75..99a4453 100644 --- a/src/__tests__/TestItem-test.tsx +++ b/src/__tests__/TestItem-test.tsx @@ -2,8 +2,7 @@ import { fireEvent, render } from '@testing-library/react-native' import React from 'react' import { ActivityIndicator } from 'react-native-paper' import { act, ReactTestInstance } from 'react-test-renderer' -import { WollokException } from 'wollok-ts/dist/interpreter/runtimeModel' -import { Body, Test } from 'wollok-ts/dist/model' +import { Body, Test, WollokException } from 'wollok-ts' import TestItem from '../components/tests/TestItem' import { theme } from '../theme' import { TestRun } from '../utils/wollok-helpers' diff --git a/src/__tests__/mocks/ProjectProviderMock.tsx b/src/__tests__/mocks/ProjectProviderMock.tsx index 9a340c4..e2fd7e4 100644 --- a/src/__tests__/mocks/ProjectProviderMock.tsx +++ b/src/__tests__/mocks/ProjectProviderMock.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { Environment, Problem } from 'wollok-ts' +import { Environment, Problem } from 'wollok-ts/dist/model' import { ProjectContext } from '../../context/ProjectProvider' import { ParentComponentProp } from '../../utils/type-helpers' @@ -11,6 +11,8 @@ export const initialContext = { actions: { addEntity: jest.fn(), addDescribe: jest.fn(), + addMember: jest.fn(), + changeMember: jest.fn(), rebuildEnvironment: jest.fn(), runTest: jest.fn(), execution: jest.fn(), diff --git a/src/pages/EntityDetails/EntityDetails.tsx b/src/components/entity-detail/ModuleDetails.tsx similarity index 60% rename from src/pages/EntityDetails/EntityDetails.tsx rename to src/components/entity-detail/ModuleDetails.tsx index 27c6d85..8ff4795 100644 --- a/src/pages/EntityDetails/EntityDetails.tsx +++ b/src/components/entity-detail/ModuleDetails.tsx @@ -3,22 +3,28 @@ import React, { useState } from 'react' import { ScrollView } from 'react-native-gesture-handler' import { List } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' -import { Field, is, Method } from 'wollok-ts/dist/model' -import { AccordionList } from '../../components/entity-detail/AccordionList' -import AttributeItemComponent from '../../components/entity-detail/AttributeItem/AttributeItem' -import NewAttributeModal from '../../components/entity-detail/new-attribute-modal/NewAttributeModal' -import NewMethodModal from '../../components/entity-detail/new-method-modal/NewMethodModal' -import MultiFabScreen from '../../components/FabScreens/MultiFabScreen' -import { ProblemReporterButton } from '../../components/problems/ProblemReporterButton' -import { useEntity } from '../../context/EntityProvider' +import { Describe, Field, is, Method, Module } from 'wollok-ts/dist/model' +import { AccordionList } from './AccordionList' +import AttributeItemComponent from './AttributeItem/AttributeItem' +import NewAttributeModal from './new-attribute-modal/NewAttributeModal' +import NewMethodModal from './new-method-modal/NewMethodModal' +import MultiFabScreen from '../FabScreens/MultiFabScreen' +import { ProblemReporterButton } from '../problems/ProblemReporterButton' +import { useProject } from '../../context/ProjectProvider' import { wTranslate } from '../../utils/translation-helpers' import { methodFQN, methodLabel } from '../../utils/wollok-helpers' -import { EntityMemberScreenNavigationProp } from '../EntityMemberDetail' +import { EditorScreenNavigationProp } from '../../pages/Editor' -export const EntityDetails = function () { +export type ModuleDetailsProps = { + module: Exclude +} + +export const ModuleDetails = function ({ module }: ModuleDetailsProps) { const [methodModalVisible, setMethodModalVisible] = useState(false) const [attributeModalVisible, setAttributeModalVisible] = useState(false) - const { entity } = useEntity() + const { + actions: { addMember }, + } = useProject() return ( title={wTranslate('entityDetails.attributes').toUpperCase()} - items={entity.members.filter(is('Field')) as Field[]} + items={module.members.filter(is('Field')) as Field[]} VisualItem={AttributeItem} initialExpanded={true} /> title={wTranslate('entityDetails.methods').toUpperCase()} - items={entity.members.filter(is('Method')) as Method[]} + items={module.members.filter(is('Method')) as Method[]} VisualItem={MethodItem} initialExpanded={true} /> @@ -53,10 +59,13 @@ export const EntityDetails = function () { ) @@ -67,18 +76,18 @@ function AttributeItem({ item: attribute }: { item: Field }) { } function MethodItem({ item: method }: { item: Method }) { - const navigator = useNavigation() + const navigator = useNavigation() + function gotoMethod() { + navigator.navigate('Editor', { + fqn: methodFQN(method), + }) + } return ( } - onPress={() => - navigator.navigate('EntityMemberDetails', { - entityMember: method, - fqn: methodFQN(method), - }) - } + onPress={gotoMethod} /> ) } diff --git a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx index 416c883..698fe28 100644 --- a/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx +++ b/src/components/entity-detail/new-attribute-modal/NewAttributeModal.tsx @@ -1,34 +1,30 @@ import React, { useState } from 'react' import { StyleSheet, View } from 'react-native' -import { Text, TextInput, withTheme } from 'react-native-paper' +import { Text, TextInput } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' import { Expression, Field } from 'wollok-ts/dist/model' -import { useEntity } from '../../../context/EntityProvider' -import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' +import { Visible } from '../../../utils/type-helpers' import CheckIcon from '../../ui/CheckIcon' import ExpressionInput from '../../ui/ExpressionInput' import FormModal from '../../ui/FormModal/FormModal' import { ATTRIBUTE_ICONS } from '../attribute-icons' -type Props = { - visible: boolean - setVisible: (visible: boolean) => void - theme: Theme +type AttributeFormModalProps = Visible & { + addNewField: (f: Field) => void + contextFQN: string } -const AttributeFormModal = (props: Props) => { - const { - actions: { addMember }, - entity, - } = useEntity() +const AttributeFormModal = ({ + visible, + setVisible, + addNewField, + contextFQN, +}: AttributeFormModalProps) => { const [name, setName] = useState('') const [isConstant, setConstant] = useState(false) const [isProperty, setProperty] = useState(false) const [initialValue, setInitialValue] = useState() - const { visible, setVisible } = props - - const styles = getStyles(props.theme) const checkboxes = [ { @@ -68,7 +64,7 @@ const AttributeFormModal = (props: Props) => { { } function newAttribute() { - addMember(new Field({ name, isConstant, isProperty, value: initialValue })) + addNewField( + new Field({ name, isConstant, isProperty, value: initialValue }), + ) } } -const getStyles = (_theme: Theme) => - StyleSheet.create({ - checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 }, - constName: { fontSize: 16 }, - }) +const styles = StyleSheet.create({ + checkbox: { flexDirection: 'row', alignItems: 'center', marginVertical: 5 }, + constName: { fontSize: 16 }, +}) -export default withTheme(AttributeFormModal) +export default AttributeFormModal diff --git a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx index febce88..b97cd7e 100644 --- a/src/components/entity-detail/new-method-modal/NewMethodModal.tsx +++ b/src/components/entity-detail/new-method-modal/NewMethodModal.tsx @@ -1,22 +1,20 @@ import React, { useState } from 'react' import { StyleSheet } from 'react-native' -import { Text, TextInput, withTheme } from 'react-native-paper' +import { Text, TextInput } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' import { Body, Method, Parameter } from 'wollok-ts/dist/model' -import { useEntity } from '../../../context/EntityProvider' -import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' +import { Visible } from '../../../utils/type-helpers' import FormModal from '../../ui/FormModal/FormModal' import ParameterInput from './ParameterInput' -const NewMethodModal = (props: { - visible: boolean - setVisible: (value: boolean) => void - theme: Theme -}) => { - const { - actions: { addMember }, - } = useEntity() +type NewMethodModalProps = Visible & { addNewMethod: (m: Method) => void } + +const NewMethodModal = ({ + visible, + setVisible, + addNewMethod, +}: NewMethodModalProps) => { const [name, setName] = useState('') const [parameters, setParameters] = useState([]) const [nextParameter, setNextParameter] = useState('') @@ -26,8 +24,8 @@ const NewMethodModal = (props: { title={wTranslate('entityDetails.methodModal.newMethod')} resetForm={reset} onSubmit={newMethod} - setVisible={props.setVisible} - visible={props.visible}> + setVisible={setVisible} + visible={visible}> () + const navigation = useNavigation() + const goToEntityDetails = (entity: Entity) => { navigation.navigate('EntityStack', { entityFQN: entity.fullyQualifiedName(), }) - - setShowProblems(false) } - const goToMethod = (method: Method) => { - navigation.navigate('EntityMemberDetails', { - entityMember: method, - fqn: methodFQN(method), + const goToEditor = (entityMember: EntityMemberWithBody) => { + navigation.navigate('Editor', { + fqn: entityMemberFQN(entityMember), }) - - setShowProblems(false) } - const goto = (n: Node): void => + const goto = (n: Node): void => { n.match({ - Method: goToMethod, + Method: goToEditor, + Test: goToEditor, Singleton: goToEntityDetails, + Describe: goToEntityDetails, Field: f => goToEntityDetails(f.parent), - Assignment: a => goto(a.parent), Body: b => goto(b.parent), + Sentence: e => goto(e.parent), Expression: e => goto(e.parent), - Test: t => goto(t.parent), - Describe: t => goToEntityDetails(t), }) + setShowProblems(false) + } + return ( void -}) { - const { - actions: { addMember }, - } = useEntity() +type NewTestModalProps = Pick & { + addNewTest: (t: Test) => void +} + +function NewTestModal({ visible, setVisible, addNewTest }: NewTestModalProps) { const [name, setName] = useState(initialName) return ( + setVisible={setVisible} + visible={visible}> () + const navigator = useNavigation() function navigateTo(test: Test) { - navigator.navigate('EntityMemberDetails', { - entityMember: test, + navigator.navigate('Editor', { fqn: test.fullyQualifiedName(), }) } @@ -63,6 +64,7 @@ export const Tests = function () { ) diff --git a/src/components/ui/Body/AssignmentFormModal.tsx b/src/components/ui/Body/AssignmentFormModal.tsx index cf1f4df..f541cf6 100644 --- a/src/components/ui/Body/AssignmentFormModal.tsx +++ b/src/components/ui/Body/AssignmentFormModal.tsx @@ -62,7 +62,7 @@ export function AssignmentFormModal({ diff --git a/src/components/ui/ExpressionInput.tsx b/src/components/ui/ExpressionInput.tsx index b665c1e..99eba36 100644 --- a/src/components/ui/ExpressionInput.tsx +++ b/src/components/ui/ExpressionInput.tsx @@ -3,7 +3,7 @@ import React from 'react' import { StyleSheet, View } from 'react-native' import { IconButton, Text, withTheme } from 'react-native-paper' import { Expression, Name } from 'wollok-ts/dist/model' -import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker/ExpressionMaker' +import { ExpressionMakerScreenProp } from '../../pages/ExpressionMaker' import { Theme } from '../../theme' import { ExpressionDisplay } from '../expressions/ExpressionDisplay' @@ -11,7 +11,7 @@ type Props = { value?: Expression setValue: (expression?: Expression) => void theme: Theme - fqn: Name + contextFQN: Name inputPlaceholder: string } @@ -21,7 +21,7 @@ const ExpressionInput = (props: Props) => { const goToExpressionMaker = () => { navigation.push('ExpressionMaker', { onSubmit: setValue, - contextFQN: props.fqn, + contextFQN: props.contextFQN, initialExpression: value, }) } diff --git a/src/components/ui/FormModal/FormModal.tsx b/src/components/ui/FormModal/FormModal.tsx index 52e24c0..e926404 100644 --- a/src/components/ui/FormModal/FormModal.tsx +++ b/src/components/ui/FormModal/FormModal.tsx @@ -9,18 +9,18 @@ import { } from 'react-native-paper' import { Theme } from '../../../theme' import { wTranslate } from '../../../utils/translation-helpers' -import { ParentComponentProp } from '../../../utils/type-helpers' +import { ParentComponentProp, Visible } from '../../../utils/type-helpers' import { stylesheet } from './styles' -export type FormModalProps = ParentComponentProp<{ - visible: boolean - setVisible: (value: boolean) => void - onSubmit: () => void - resetForm?: () => void - title?: string - valid?: boolean - theme: Theme -}> +export type FormModalProps = ParentComponentProp< + Visible & { + onSubmit: () => void + resetForm?: () => void + title?: string + valid?: boolean + theme: Theme + } +> function FormModal(props: FormModalProps) { const styles = stylesheet(props.theme) diff --git a/src/context/EntityProvider.tsx b/src/context/EntityProvider.tsx deleted file mode 100644 index 0596244..0000000 --- a/src/context/EntityProvider.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { createContext } from 'react' -import { Module } from 'wollok-ts/dist/model' -import { ParentComponentProp } from '../utils/type-helpers' -import { EntityMember } from '../utils/wollok-helpers' -import { createContextHook } from './create-context-hook' -import { useProject } from './ProjectProvider' - -export const EntityContext = createContext<{ - entity: Module - actions: Actions -} | null>(null) - -type Actions = { - addMember: (newMember: EntityMember) => void - changeMember: (oldMember: EntityMember, newMember: EntityMember) => void -} - -export function EntityProvider( - props: ParentComponentProp<{ - entity: Module - }>, -) { - const { children, entity } = props - const { - actions: { rebuildEnvironment }, - } = useProject() - - const addMember = (newMember: EntityMember) => { - rebuildEnvironment( - entity.copy({ - members: [...entity.members, newMember], - }) as Module, - ) - } - - const changeMember = (oldMember: EntityMember, newMember: EntityMember) => { - rebuildEnvironment( - entity.copy({ - members: [...entity.members.filter(m => m !== oldMember), newMember], - }) as Module, - ) - } - - const initialContext = { - entity: entity, - actions: { addMember, changeMember }, - } - return ( - - {children} - - ) -} - -export const useEntity = createContextHook(EntityContext, { - hookName: 'useEntity', - contextName: 'EntityProvider', -}) diff --git a/src/context/ProjectProvider.tsx b/src/context/ProjectProvider.tsx index 4ca618b..164a3ba 100644 --- a/src/context/ProjectProvider.tsx +++ b/src/context/ProjectProvider.tsx @@ -17,7 +17,12 @@ import { import validate from 'wollok-ts/dist/validator' import { saveProject } from '../services/persistance.service' import { ParentComponentProp } from '../utils/type-helpers' -import { executionFor, interpretTest, TestRun } from '../utils/wollok-helpers' +import { + EntityMember, + executionFor, + interpretTest, + TestRun, +} from '../utils/wollok-helpers' import { createContextHook } from './create-context-hook' export const mainPackageName = 'main' @@ -32,9 +37,13 @@ export const ProjectContext = createContext<{ } | null>(null) type Actions = { + rebuildEnvironment: (entity: Entity) => void addEntity: (module: Module) => void addDescribe: (test: Describe) => void - rebuildEnvironment: (entity: Entity) => void + addMember: (parent: Module) => (newMember: EntityMember) => void + changeMember: ( + parent: Module, + ) => (oldMember: EntityMember, newMember: EntityMember) => void runTest: (test: Test) => TestRun execution: (test: Test) => ExecutionDirector save: () => Promise @@ -74,28 +83,20 @@ export function ProjectProvider( return link([pack], base ?? project) } - function addEntity(newEntity: Module) { - rebuildEnvironment(newEntity) - } - - function addDescribe(newDescribe: Describe) { - rebuildEnvironment(newDescribe) - } - function rebuildEnvironment(entity: Entity) { const packageName = entity.is('Describe') ? testsPackageName : mainPackageName const newProject = buildEnvironment(packageName, [entity]) setProject(newProject) - setProblems(validateProject(newProject) as Problem[]) setChanged(true) + setProblems(validateProject(newProject) as Problem[]) } function validateProject(_project: Environment) { const targetPackages = [ - project.getNodeByFQN(mainPackageName), - project.getNodeByFQN(testsPackageName), + _project.getNodeByFQN(mainPackageName), + _project.getNodeByFQN(testsPackageName), ] const belongsToTargetProject = (p: Problem) => targetPackages.some(target => p.node.ancestors().includes(target)) @@ -105,6 +106,35 @@ export function ProjectProvider( /////////////////////////////////// BUILD ////////////////////////////////// + /////////////////////////////////// ENTITIES ////////////////////////////////// + + function addEntity(newEntity: Module) { + rebuildEnvironment(newEntity) + } + + function addDescribe(newDescribe: Describe) { + rebuildEnvironment(newDescribe) + } + + const addMember = (entity: Module) => (newMember: EntityMember) => { + rebuildEnvironment( + entity.copy({ + members: [...entity.members, newMember], + }) as Module, + ) + } + + const changeMember = + (entity: Module) => (oldMember: EntityMember, newMember: EntityMember) => { + rebuildEnvironment( + entity.copy({ + members: [...entity.members.filter(m => m !== oldMember), newMember], + }) as Module, + ) + } + + /////////////////////////////////// ENTITIES ////////////////////////////////// + /////////////////////////////////// EXECUTION ////////////////////////////////// function runTest(test: Test) { @@ -130,6 +160,8 @@ export function ProjectProvider( actions: { addEntity, addDescribe, + addMember, + changeMember, rebuildEnvironment, runTest, execution, diff --git a/src/pages/NewMessageCall.tsx b/src/pages/ArgumentsMaker.tsx similarity index 86% rename from src/pages/NewMessageCall.tsx rename to src/pages/ArgumentsMaker.tsx index dd7b9f2..93137fd 100644 --- a/src/pages/NewMessageCall.tsx +++ b/src/pages/ArgumentsMaker.tsx @@ -7,14 +7,15 @@ import { Expression, Send } from 'wollok-ts/dist/model' import ExpressionInput from '../components/ui/ExpressionInput' import { SubmitCheckButton } from '../components/ui/Header' import { wTranslate } from '../utils/translation-helpers' -import { EntityStackParamList } from './EntityStack' +import { methodLabel } from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' -export function NewMessageCall({ +export function ArgumentsMaker({ route: { params: { method, receiver, contextFQN, onSubmit }, }, }: { - route: RouteProp + route: RouteProp }) { const [args, setArguments] = useState<(Expression | undefined)[]>( method.parameters.map(() => undefined), @@ -24,6 +25,7 @@ export function NewMessageCall({ React.useLayoutEffect(() => { navigation.setOptions({ + title: methodLabel(method), //TODO: Show receiver? headerRight: () => ( a === undefined)} @@ -53,7 +55,7 @@ export function NewMessageCall({ {_.name} setParameter(i, expression)} value={args[i]} inputPlaceholder={upperCaseFirst( diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx new file mode 100644 index 0000000..d9b67d8 --- /dev/null +++ b/src/pages/Editor.tsx @@ -0,0 +1,56 @@ +import { RouteProp, useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' +import React from 'react' +import { Body } from 'wollok-ts/dist/model' +import { BodyMaker } from '../components/ui/Body/BodyMaker' +import { useProject } from '../context/ProjectProvider' +import { + allScopedVariables, + entityMemberByFQN, + EntityMemberWithBody, +} from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' + +export type EditorScreenNavigationProp = StackNavigationProp< + ProjectStackParamList, + 'Editor' +> + +type Route = RouteProp + +export const Editor = ({ + route: { + params: { fqn }, + }, +}: { + route: Route +}) => { + const { + project, + actions: { changeMember }, + } = useProject() + const entity = entityMemberByFQN(project, fqn) + const parent = entity.parent + + const navigation = useNavigation() + React.useLayoutEffect(() => { + navigation.setOptions({ + title: entity.name, + headerTitleAlign: 'center', + animationEnabled: false, + }) + }, [navigation, entity]) + + function setBody(body: Body) { + changeMember(parent)(entity, entity.copy({ body }) as EntityMemberWithBody) + } + + return ( + + ) +} diff --git a/src/pages/EntityMemberDetail.tsx b/src/pages/EntityMemberDetail.tsx deleted file mode 100644 index afb6a92..0000000 --- a/src/pages/EntityMemberDetail.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { RouteProp } from '@react-navigation/native' -import { StackNavigationProp } from '@react-navigation/stack' -import React from 'react' -import { Body } from 'wollok-ts/dist/model' -import { BodyMaker } from '../components/ui/Body/BodyMaker' -import { useEntity } from '../context/EntityProvider' -import { - allScopedVariables, - EntityMemberWithBody, -} from '../utils/wollok-helpers' -import { EntityStackParamList } from './EntityStack' - -export type EntityMemberScreenNavigationProp = StackNavigationProp< - EntityStackParamList, - 'EntityMemberDetails' -> - -type Route = RouteProp - -export const EntityMemberDetail = ({ - route: { - params: { entityMember, fqn }, - }, -}: { - route: Route -}) => { - const { - actions: { changeMember }, - } = useEntity() - - function setBody(body: Body) { - changeMember( - entityMember, - entityMember.copy({ body }) as EntityMemberWithBody, - ) - } - - return ( - - ) -} diff --git a/src/pages/EntityStack.tsx b/src/pages/EntityStack.tsx index 5799353..8f0cdb5 100644 --- a/src/pages/EntityStack.tsx +++ b/src/pages/EntityStack.tsx @@ -1,95 +1,30 @@ -import { RouteProp } from '@react-navigation/native' -import { createStackNavigator } from '@react-navigation/stack' +import { RouteProp, useNavigation } from '@react-navigation/native' import React from 'react' -import { Expression, Method, Module, Name, Send } from 'wollok-ts/dist/model' +import { Module } from 'wollok-ts/dist/model' +import { ModuleDetails } from '../components/entity-detail/ModuleDetails' import { Tests } from '../components/tests/Tests' -import { EntityProvider } from '../context/EntityProvider' import { useProject } from '../context/ProjectProvider' -import { wTranslate } from '../utils/translation-helpers' -import { - entityMemberLabel, - EntityMemberWithBody, -} from '../utils/wollok-helpers' -import { EntityDetails } from './EntityDetails/EntityDetails' -import { EntityMemberDetail } from './EntityMemberDetail' -import ExpressionMaker, { - ExpressionOnSubmit, -} from './ExpressionMaker/ExpressionMaker' -import { NewMessageCall } from './NewMessageCall' import { ProjectStackParamList } from './ProjectNavigator' export type EntityStackRoute = RouteProp -export type EntityStackParamList = { - EntityDetails: undefined - Tests: undefined - EntityMemberDetails: { - entityMember: EntityMemberWithBody - fqn: Name - } - ExpressionMaker: { - onSubmit: ExpressionOnSubmit - contextFQN: Name - initialExpression?: Expression - } - NewMessageSend: { - receiver: Expression - method: Method - contextFQN: Name - onSubmit: (s: Send) => void - } -} - -export default function (props: { route: EntityStackRoute }) { +function EntityStack(props: { route: EntityStackRoute }) { const { project } = useProject() - const Stack = createStackNavigator() const entity = project.getNodeByFQN(props.route.params.entityFQN) - return ( - - - {entity.is('Describe') ? ( - - ) : ( - - )} - ({ - title: entityMemberLabel(methodRoute.params.entityMember), - })} - /> - - ({ title: route.route.params.method.name })} - /> - - + + const navigation = useNavigation() + React.useLayoutEffect(() => { + navigation.setOptions({ + title: entity.name, + headerTitleAlign: 'center', + }) + }, [navigation, entity]) + + return entity.is('Describe') ? ( + + ) : ( + ) } + +export default EntityStack diff --git a/src/pages/ExpressionMaker/ExpressionMaker.tsx b/src/pages/ExpressionMaker.tsx similarity index 72% rename from src/pages/ExpressionMaker/ExpressionMaker.tsx rename to src/pages/ExpressionMaker.tsx index 33b120b..66e9e6c 100644 --- a/src/pages/ExpressionMaker/ExpressionMaker.tsx +++ b/src/pages/ExpressionMaker.tsx @@ -4,30 +4,25 @@ import React, { useState } from 'react' import { StyleSheet, View } from 'react-native' import { ScrollView } from 'react-native-gesture-handler' import { Button, List, TextInput } from 'react-native-paper' -import { Expression, Module } from 'wollok-ts/dist/model' -import { ListLiterals } from '../../components/expressions/expression-lists/literals-list' -import { ListMessages } from '../../components/expressions/expression-lists/messages-list' -import { ListSingletons } from '../../components/expressions/expression-lists/singletons-list' -import { ListVariables } from '../../components/expressions/expression-lists/variables-list' -import { ExpressionDisplay } from '../../components/expressions/ExpressionDisplay' -import { SubmitCheckButton } from '../../components/ui/Header' +import { Expression } from 'wollok-ts/dist/model' +import { ListLiterals } from '../components/expressions/expression-lists/literals-list' +import { ListMessages } from '../components/expressions/expression-lists/messages-list' +import { ListSingletons } from '../components/expressions/expression-lists/singletons-list' +import { ListVariables } from '../components/expressions/expression-lists/variables-list' +import { ExpressionDisplay } from '../components/expressions/ExpressionDisplay' +import { SubmitCheckButton } from '../components/ui/Header' import { Context, ExpressionContextProvider, useExpressionContext, -} from '../../context/ExpressionContextProvider' -import { useProject } from '../../context/ProjectProvider' -import { wTranslate } from '../../utils/translation-helpers' -import { isMethodFQN, methodByFQN } from '../../utils/wollok-helpers' -import { EntityStackParamList } from '../EntityStack' - -export type ExpressionMakerProp = RouteProp< - EntityStackParamList, - 'ExpressionMaker' -> +} from '../context/ExpressionContextProvider' +import { useProject } from '../context/ProjectProvider' +import { wTranslate } from '../utils/translation-helpers' +import { entityMemberByFQN } from '../utils/wollok-helpers' +import { ProjectStackParamList } from './ProjectNavigator' export type ExpressionMakerScreenProp = StackNavigationProp< - EntityStackParamList, + ProjectStackParamList, 'ExpressionMaker' > @@ -55,6 +50,9 @@ function ExpressionMaker(props: { const navigation = useNavigation() React.useLayoutEffect(() => { navigation.setOptions({ + title: wTranslate('expression.title'), + headerTitleAlign: 'center', + animationEnabled: false, headerRight: () => ( + const styles = StyleSheet.create({ view: { display: 'flex', maxHeight: '85%' }, }) @@ -124,12 +127,11 @@ export default function ({ params: { contextFQN, onSubmit, initialExpression }, }, }: { - route: RouteProp + route: ExpressionMakerRouteProp }) { const { project } = useProject() - const context: Context = isMethodFQN(contextFQN) - ? methodByFQN(project, contextFQN) - : project.getNodeByFQN(contextFQN) + const context: Context = entityMemberByFQN(project, contextFQN) + return ( }} /> void + } } const Stack = createStackNavigator() @@ -26,11 +43,10 @@ export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { initialProject={route.params.project}> - + + + + ) diff --git a/src/pages/Describes.tsx b/src/pages/tabs/Describes.tsx similarity index 73% rename from src/pages/Describes.tsx rename to src/pages/tabs/Describes.tsx index 4493ce6..4100bee 100644 --- a/src/pages/Describes.tsx +++ b/src/pages/tabs/Describes.tsx @@ -1,10 +1,10 @@ import React, { useState } from 'react' import { ScrollView } from 'react-native-gesture-handler' import { Describe, Package } from 'wollok-ts/dist/model' -import FabAddScreen from '../components/FabScreens/FabAddScreen' -import NewDescribeModal from '../components/tests/NewDescribeModal' -import DescribeItem from '../components/tests/DescribeItem' -import { useProject } from '../context/ProjectProvider' +import FabAddScreen from '../../components/FabScreens/FabAddScreen' +import NewDescribeModal from '../../components/tests/NewDescribeModal' +import DescribeItem from '../../components/tests/DescribeItem' +import { useProject } from '../../context/ProjectProvider' export function Describes() { const { diff --git a/src/pages/Entities/Entities.tsx b/src/pages/tabs/Modules.tsx similarity index 97% rename from src/pages/Entities/Entities.tsx rename to src/pages/tabs/Modules.tsx index a9262a4..5c72f03 100644 --- a/src/pages/Entities/Entities.tsx +++ b/src/pages/tabs/Modules.tsx @@ -6,7 +6,7 @@ import NewEntityModal from '../../components/entities/NewEntityModal/NewEntityMo import FabAddScreen from '../../components/FabScreens/FabAddScreen' import { mainPackageName, useProject } from '../../context/ProjectProvider' -export function Entities() { +export function Modules() { const { project, actions: { addEntity }, diff --git a/src/utils/type-helpers.ts b/src/utils/type-helpers.ts index fadabfe..174fd7d 100644 --- a/src/utils/type-helpers.ts +++ b/src/utils/type-helpers.ts @@ -9,3 +9,8 @@ export type Maybe = T | undefined export type ParentComponentProp = T & { children: OneOrMany } + +export type Visible = { + visible: boolean + setVisible: (value: boolean) => void +} diff --git a/src/utils/wollok-helpers.ts b/src/utils/wollok-helpers.ts index d479504..3c3512e 100644 --- a/src/utils/wollok-helpers.ts +++ b/src/utils/wollok-helpers.ts @@ -56,14 +56,6 @@ export function methodLabel(method: Method): string { return `${method.name}(${method.parameters.map(_ => _.name).join(',')})` } -export function entityMemberLabel(node: EntityMemberWithBody): string { - return node.is('Method') ? methodLabel(node) : node.name -} - -export function entityMemberFQN(node: EntityMemberWithBody): string { - return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName() -} - export function literalClassFQN(literal: Literal): Name { return `wollok.lang.${upperCaseFirst(typeof literal.value)}` } @@ -78,6 +70,8 @@ export function allScopedVariables( return [...fields, ...params, ...methodVars] } +// METHODS + export function methodFQN(method: Method) { return `${method.parent.fullyQualifiedName()}.${method.name}/${ method.parameters.length @@ -102,10 +96,31 @@ export function methodByFQN(environment: Environment, fqn: Name): Method { return entity.lookupMethod(methodName, Number.parseInt(methodArity, 10))! } +export function entityMemberLabel(node: EntityMemberWithBody): string { + return node.is('Method') ? methodLabel(node) : node.name +} + +export function entityMemberFQN(node: EntityMemberWithBody): string { + return node.is('Method') ? methodFQN(node) : node.fullyQualifiedName() +} + +export function entityMemberByFQN( + environment: Environment, + fqn: Name, +): EntityMemberWithBody { + return isMethodFQN(fqn) + ? methodByFQN(environment, fqn) + : environment.getNodeByFQN(fqn) +} + +// PROBLEMS + export function isError(problem: Problem): boolean { return problem.level === 'error' } +// TESTS + export type TestResult = 'Passed' | 'Failure' | 'Error' export type TestRun = { result: TestResult; exception?: WollokException } export function interpretTest(test: Test, environment: Environment): TestRun { From 647929e23680c5ba28da5817df927f5a5dbccd11 Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 24 Apr 2022 22:29:27 +0200 Subject: [PATCH 14/16] Rename: EntityStack -> EntityDetails --- src/components/entities/Entity/Entity.tsx | 2 +- src/components/projects/ProjectHeader.tsx | 2 +- src/components/tests/DescribeItem.tsx | 2 +- src/pages/{EntityStack.tsx => EntityDetails.tsx} | 9 ++++++--- src/pages/ProjectNavigator.tsx | 6 +++--- 5 files changed, 12 insertions(+), 9 deletions(-) rename src/pages/{EntityStack.tsx => EntityDetails.tsx} (81%) diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx index 38f67af..8a8f7ab 100644 --- a/src/components/entities/Entity/Entity.tsx +++ b/src/components/entities/Entity/Entity.tsx @@ -17,7 +17,7 @@ function EntityComponent({ entity, theme }: EntityComponentProps) { const styles = stylesheet(theme) const navigation = useNavigation() const goToEntityDetails = () => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: entity.fullyQualifiedName(), }) } diff --git a/src/components/projects/ProjectHeader.tsx b/src/components/projects/ProjectHeader.tsx index 7ec5163..31570c4 100644 --- a/src/components/projects/ProjectHeader.tsx +++ b/src/components/projects/ProjectHeader.tsx @@ -27,7 +27,7 @@ export function ProjectHeader({ pushMessage }: ProjectHeaderProp) { const navigation = useNavigation() const goToEntityDetails = (entity: Entity) => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: entity.fullyQualifiedName(), }) } diff --git a/src/components/tests/DescribeItem.tsx b/src/components/tests/DescribeItem.tsx index ba4133b..40afa3b 100644 --- a/src/components/tests/DescribeItem.tsx +++ b/src/components/tests/DescribeItem.tsx @@ -17,7 +17,7 @@ function DescribeItem({ describe, theme }: Props) { const styles = stylesheet(theme) const navigation = useNavigation() const goToEntityDetails = () => { - navigation.navigate('EntityStack', { + navigation.navigate('EntityDetails', { entityFQN: describe.fullyQualifiedName(), }) } diff --git a/src/pages/EntityStack.tsx b/src/pages/EntityDetails.tsx similarity index 81% rename from src/pages/EntityStack.tsx rename to src/pages/EntityDetails.tsx index 8f0cdb5..ef0fb15 100644 --- a/src/pages/EntityStack.tsx +++ b/src/pages/EntityDetails.tsx @@ -6,9 +6,12 @@ import { Tests } from '../components/tests/Tests' import { useProject } from '../context/ProjectProvider' import { ProjectStackParamList } from './ProjectNavigator' -export type EntityStackRoute = RouteProp +export type EntityDetailsRoute = RouteProp< + ProjectStackParamList, + 'EntityDetails' +> -function EntityStack(props: { route: EntityStackRoute }) { +function EntityDetails(props: { route: EntityDetailsRoute }) { const { project } = useProject() const entity = project.getNodeByFQN(props.route.params.entityFQN) @@ -27,4 +30,4 @@ function EntityStack(props: { route: EntityStackRoute }) { ) } -export default EntityStack +export default EntityDetails diff --git a/src/pages/ProjectNavigator.tsx b/src/pages/ProjectNavigator.tsx index e3f168d..0d08882 100644 --- a/src/pages/ProjectNavigator.tsx +++ b/src/pages/ProjectNavigator.tsx @@ -6,13 +6,13 @@ import { RootStackParamList } from '../App' import { ProjectProvider } from '../context/ProjectProvider' import { ArgumentsMaker } from './ArgumentsMaker' import { Editor } from './Editor' -import EntityStack from './EntityStack' +import EntityDetails from './EntityDetails' import ExpressionMaker, { ExpressionOnSubmit } from './ExpressionMaker' import { Home } from './Home' export type ProjectStackParamList = { Home: undefined - EntityStack: { entityFQN: Name } + EntityDetails: { entityFQN: Name } Editor: { fqn: Name } @@ -43,7 +43,7 @@ export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { initialProject={route.params.project}> - + From a928462b6707df13612a24ff16e34ecc8757417e Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 24 Apr 2022 22:58:19 +0200 Subject: [PATCH 15/16] useNodeNavigation --- src/components/entities/Entity/Entity.tsx | 11 ++--- src/components/projects/ProjectHeader.tsx | 35 ++------------- src/components/tests/DescribeItem.tsx | 10 ++--- src/context/NodeNavigation.tsx | 52 +++++++++++++++++++++++ src/pages/ProjectNavigator.tsx | 35 +++++++++------ src/pages/SelectProject.tsx | 3 +- 6 files changed, 87 insertions(+), 59 deletions(-) create mode 100644 src/context/NodeNavigation.tsx diff --git a/src/components/entities/Entity/Entity.tsx b/src/components/entities/Entity/Entity.tsx index 8a8f7ab..52260a5 100644 --- a/src/components/entities/Entity/Entity.tsx +++ b/src/components/entities/Entity/Entity.tsx @@ -1,8 +1,7 @@ -import { useNavigation } from '@react-navigation/native' import React from 'react' import { List, withTheme } from 'react-native-paper' import { Module } from 'wollok-ts/dist/model' -import { HomeScreenNavigationProp } from '../../../pages/Home' +import { useNodeNavigation } from '../../../context/NodeNavigation' import { Theme } from '../../../theme' import { ProblemReporterButton } from '../../problems/ProblemReporterButton' import { EntityKindIcon } from '../EntityKindIcon' @@ -15,12 +14,8 @@ type EntityComponentProps = { function EntityComponent({ entity, theme }: EntityComponentProps) { const styles = stylesheet(theme) - const navigation = useNavigation() - const goToEntityDetails = () => { - navigation.navigate('EntityDetails', { - entityFQN: entity.fullyQualifiedName(), - }) - } + const { goToNode } = useNodeNavigation() + const goToEntityDetails = () => goToNode(entity) return ( () - - const goToEntityDetails = (entity: Entity) => { - navigation.navigate('EntityDetails', { - entityFQN: entity.fullyQualifiedName(), - }) - } - const goToEditor = (entityMember: EntityMemberWithBody) => { - navigation.navigate('Editor', { - fqn: entityMemberFQN(entityMember), - }) - } + const { goToNode } = useNodeNavigation() const goto = (n: Node): void => { - n.match({ - Method: goToEditor, - Test: goToEditor, - Singleton: goToEntityDetails, - Describe: goToEntityDetails, - Field: f => goToEntityDetails(f.parent), - Body: b => goto(b.parent), - Sentence: e => goto(e.parent), - Expression: e => goto(e.parent), - }) - + goToNode(n) setShowProblems(false) } diff --git a/src/components/tests/DescribeItem.tsx b/src/components/tests/DescribeItem.tsx index 40afa3b..d34e1ab 100644 --- a/src/components/tests/DescribeItem.tsx +++ b/src/components/tests/DescribeItem.tsx @@ -1,7 +1,7 @@ -import { useNavigation } from '@react-navigation/native' import React from 'react' import { List, withTheme } from 'react-native-paper' import { Describe } from 'wollok-ts/dist/model' +import { useNodeNavigation } from '../../context/NodeNavigation' import { Theme } from '../../theme' import { stylesheet } from '../entities/Entity/styles' import IconImage from '../ui/IconImage' @@ -15,12 +15,8 @@ type Props = { // TODO: Merge with Entity component function DescribeItem({ describe, theme }: Props) { const styles = stylesheet(theme) - const navigation = useNavigation() - const goToEntityDetails = () => { - navigation.navigate('EntityDetails', { - entityFQN: describe.fullyQualifiedName(), - }) - } + const { goToNode } = useNodeNavigation() + const goToEntityDetails = () => goToNode(describe) return ( void +} | null>(null) + +export function NodeNavigationProvider({ children }: ParentComponentProp) { + const navigation = useNavigation() + + const goToEntityDetails = (entity: Entity) => { + navigation.navigate('EntityDetails', { + entityFQN: entity.fullyQualifiedName(), + }) + } + const goToEditor = (entityMember: EntityMemberWithBody) => { + navigation.navigate('Editor', { + fqn: entityMemberFQN(entityMember), + }) + } + + const goToNode = (n: Node): void => { + n.match({ + Method: goToEditor, + Test: goToEditor, + Describe: goToEntityDetails, + Module: goToEntityDetails, + Field: f => goToEntityDetails(f.parent), + Body: b => goToNode(b.parent), + Sentence: e => goToNode(e.parent), + Expression: e => goToNode(e.parent), + }) + } + + const init = { goToNode } + + return ( + {children} + ) +} + +export const useNodeNavigation = createContextHook(ContextContext, { + contextName: 'NodeNavigationProvider', + hookName: 'useNodeNavigation', +}) diff --git a/src/pages/ProjectNavigator.tsx b/src/pages/ProjectNavigator.tsx index 0d08882..c46e715 100644 --- a/src/pages/ProjectNavigator.tsx +++ b/src/pages/ProjectNavigator.tsx @@ -1,8 +1,12 @@ import { RouteProp } from '@react-navigation/core' -import { createStackNavigator } from '@react-navigation/stack' +import { + createStackNavigator, + StackNavigationProp, +} from '@react-navigation/stack' import React from 'react' import { Expression, Method, Name, Send } from 'wollok-ts/dist/model' import { RootStackParamList } from '../App' +import { NodeNavigationProvider } from '../context/NodeNavigation' import { ProjectProvider } from '../context/ProjectProvider' import { ArgumentsMaker } from './ArgumentsMaker' import { Editor } from './Editor' @@ -36,19 +40,26 @@ export type ProjectStackRoute = RouteProp< 'ProjectNavigator' > +export type ProjectScreenNavigationProp = StackNavigationProp< + RootStackParamList, + 'ProjectNavigator' +> + export function ProjectNavigator({ route }: { route: ProjectStackRoute }) { return ( - - - - - - - - - + + + + + + + + + + + ) } diff --git a/src/pages/SelectProject.tsx b/src/pages/SelectProject.tsx index d3022d8..64fb4b5 100644 --- a/src/pages/SelectProject.tsx +++ b/src/pages/SelectProject.tsx @@ -13,12 +13,13 @@ import { saveProject, } from '../services/persistance.service' import { useTheme } from '../theme' +import { ProjectScreenNavigationProp } from './ProjectNavigator' export function SelectProject() { const [projects, setProjects] = useState([]) const [showNewProjectModal, setShowNewProjectModal] = useState(false) const focused = useIsFocused() - const navigation = useNavigation() + const navigation = useNavigation() const theme = useTheme() From 649703f5bd21bfa80ef6894ad169b6e61a477a4d Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 24 Apr 2022 23:28:04 +0200 Subject: [PATCH 16/16] Fix and reorganize tests --- jest.config.js | 2 +- src/__tests__/{App-test.tsx => App.spec.tsx} | 0 .../{Problems-test.tsx => Problems.spec.tsx} | 7 +++++-- .../{TestItem-test.tsx => TestItem.spec.tsx} | 2 +- .../tests => __tests__}/Variable.spec.tsx | 4 ++-- .../tests => __tests__}/VisualSentence.spec.tsx | 12 ++++++------ .../{mocks => utils}/ProjectProviderMock.tsx | 0 src/__tests__/utils/test-helpers.tsx | 14 ++++++++++++++ src/__tests__/{mocks => utils}/wollokProject.ts | 4 ++++ .../{ui => }/Body/AssignmentFormModal.tsx | 8 ++++---- src/components/{ui => }/Body/BodyMaker.tsx | 10 +++++----- src/components/{ui => }/Body/VariableForm.tsx | 12 ++++++------ .../{ui => }/Body/sentences/Assignment.tsx | 8 ++++---- .../Body/sentences/ReferenceExpression.tsx | 4 ++-- src/components/{ui => }/Body/sentences/Return.tsx | 8 ++++---- src/components/{ui => }/Body/sentences/Send.tsx | 6 +++--- .../{ui => }/Body/sentences/Variable.tsx | 10 +++++----- .../{ui => }/Body/sentences/VisualSentence.tsx | 4 ++-- src/components/problems/ProblemReporterButton.tsx | 1 + src/pages/Editor.tsx | 2 +- src/utils/test-helpers.tsx | 11 ----------- 21 files changed, 70 insertions(+), 59 deletions(-) rename src/__tests__/{App-test.tsx => App.spec.tsx} (100%) rename src/__tests__/{Problems-test.tsx => Problems.spec.tsx} (94%) rename src/__tests__/{TestItem-test.tsx => TestItem.spec.tsx} (98%) rename src/{components/ui/Body/sentences/tests => __tests__}/Variable.spec.tsx (92%) rename src/{components/ui/Body/sentences/tests => __tests__}/VisualSentence.spec.tsx (80%) rename src/__tests__/{mocks => utils}/ProjectProviderMock.tsx (100%) create mode 100644 src/__tests__/utils/test-helpers.tsx rename src/__tests__/{mocks => utils}/wollokProject.ts (95%) rename src/components/{ui => }/Body/AssignmentFormModal.tsx (89%) rename src/components/{ui => }/Body/BodyMaker.tsx (90%) rename src/components/{ui => }/Body/VariableForm.tsx (83%) rename src/components/{ui => }/Body/sentences/Assignment.tsx (64%) rename src/components/{ui => }/Body/sentences/ReferenceExpression.tsx (76%) rename src/components/{ui => }/Body/sentences/Return.tsx (69%) rename src/components/{ui => }/Body/sentences/Send.tsx (59%) rename src/components/{ui => }/Body/sentences/Variable.tsx (69%) rename src/components/{ui => }/Body/sentences/VisualSentence.tsx (88%) delete mode 100644 src/utils/test-helpers.tsx diff --git a/jest.config.js b/jest.config.js index 71d4036..7aae693 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,7 @@ module.exports = { '^.+\\.ts?$': 'ts-jest', }, moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - modulePathIgnorePatterns: ['mocks'], + modulePathIgnorePatterns: ['utils'], setupFilesAfterEnv: [ "@testing-library/jest-native/extend-expect", "./setup-tests.js" diff --git a/src/__tests__/App-test.tsx b/src/__tests__/App.spec.tsx similarity index 100% rename from src/__tests__/App-test.tsx rename to src/__tests__/App.spec.tsx diff --git a/src/__tests__/Problems-test.tsx b/src/__tests__/Problems.spec.tsx similarity index 94% rename from src/__tests__/Problems-test.tsx rename to src/__tests__/Problems.spec.tsx index 5e77c4f..749efb0 100644 --- a/src/__tests__/Problems-test.tsx +++ b/src/__tests__/Problems.spec.tsx @@ -5,7 +5,7 @@ import { ProblemIcon } from '../components/problems/ProblemIcon' import { ProblemReporterButton } from '../components/problems/ProblemReporterButton' import { ProblemModal } from '../components/problems/ProblemsModal' import { methodFQN } from '../utils/wollok-helpers' -import ProjectProviderMock from './mocks/ProjectProviderMock' +import ProjectProviderMock from './utils/ProjectProviderMock' import { clazz, error, @@ -16,7 +16,8 @@ import { singleton, test, warning, -} from './mocks/wollokProject' + wReturn, +} from './utils/wollokProject' describe('ProblemIcon', () => { it('is red on error', () => { @@ -66,6 +67,8 @@ describe('ProblemReporterButton', () => { iconExistTest('inside method body', method, error) iconExistTest('inside test body', test, problem(test.sentences()[0])) + + iconExistTest('inside return expression', wReturn, problem(wReturn.value!)) }) }) diff --git a/src/__tests__/TestItem-test.tsx b/src/__tests__/TestItem.spec.tsx similarity index 98% rename from src/__tests__/TestItem-test.tsx rename to src/__tests__/TestItem.spec.tsx index 99a4453..0d2051e 100644 --- a/src/__tests__/TestItem-test.tsx +++ b/src/__tests__/TestItem.spec.tsx @@ -6,7 +6,7 @@ import { Body, Test, WollokException } from 'wollok-ts' import TestItem from '../components/tests/TestItem' import { theme } from '../theme' import { TestRun } from '../utils/wollok-helpers' -import ProjectProviderMock from './mocks/ProjectProviderMock' +import ProjectProviderMock from './utils/ProjectProviderMock' const testMock = new Test({ name: 'TEST', diff --git a/src/components/ui/Body/sentences/tests/Variable.spec.tsx b/src/__tests__/Variable.spec.tsx similarity index 92% rename from src/components/ui/Body/sentences/tests/Variable.spec.tsx rename to src/__tests__/Variable.spec.tsx index 96148ce..5c85aaa 100644 --- a/src/components/ui/Body/sentences/tests/Variable.spec.tsx +++ b/src/__tests__/Variable.spec.tsx @@ -1,7 +1,7 @@ import React from 'react' import { Literal, Variable as VariableModel } from 'wollok-ts/dist/model' -import { renderWithTheme } from '../../../../../utils/test-helpers' -import { Variable } from '../Variable' +import { renderWithTheme } from './utils/test-helpers' +import { Variable } from '../components/Body/sentences/Variable' function renderVariable(variable: VariableModel) { const { UNSAFE_queryByProps, getByText } = renderWithTheme( diff --git a/src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx b/src/__tests__/VisualSentence.spec.tsx similarity index 80% rename from src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx rename to src/__tests__/VisualSentence.spec.tsx index 0eacd76..4313337 100644 --- a/src/components/ui/Body/sentences/tests/VisualSentence.spec.tsx +++ b/src/__tests__/VisualSentence.spec.tsx @@ -9,12 +9,12 @@ import { Sentence, Variable as VariableModel, } from 'wollok-ts/dist/model' -import { renderWithTheme } from '../../../../../utils/test-helpers' -import { Assignment } from '../Assignment' -import { Return } from '../Return' -import { Send } from '../Send' -import { Variable } from '../Variable' -import { VisualSentence } from '../VisualSentence' +import { Assignment } from '../components/Body/sentences/Assignment' +import { Return } from '../components/Body/sentences/Return' +import { Send } from '../components/Body/sentences/Send' +import { Variable } from '../components/Body/sentences/Variable' +import { VisualSentence } from '../components/Body/sentences/VisualSentence' +import { renderWithTheme } from './utils/test-helpers' describe('matching sentences with components', () => { it('should match a send sentence', () => { diff --git a/src/__tests__/mocks/ProjectProviderMock.tsx b/src/__tests__/utils/ProjectProviderMock.tsx similarity index 100% rename from src/__tests__/mocks/ProjectProviderMock.tsx rename to src/__tests__/utils/ProjectProviderMock.tsx diff --git a/src/__tests__/utils/test-helpers.tsx b/src/__tests__/utils/test-helpers.tsx new file mode 100644 index 0000000..65ebec8 --- /dev/null +++ b/src/__tests__/utils/test-helpers.tsx @@ -0,0 +1,14 @@ +import { NavigationContainer } from '@react-navigation/native' +import { render } from '@testing-library/react-native' +import React from 'react' +import { theme } from '../../theme' +import ProjectProviderMock from './ProjectProviderMock' +import { OneOrMany } from '../../utils/type-helpers' + +export function renderWithTheme(children: OneOrMany) { + return render( + + {children} + , + ) +} diff --git a/src/__tests__/mocks/wollokProject.ts b/src/__tests__/utils/wollokProject.ts similarity index 95% rename from src/__tests__/mocks/wollokProject.ts rename to src/__tests__/utils/wollokProject.ts index 644e0fb..59b8d04 100644 --- a/src/__tests__/mocks/wollokProject.ts +++ b/src/__tests__/utils/wollokProject.ts @@ -3,6 +3,7 @@ import { Class, Describe, Environment, + Expression, Field, Import, link, @@ -13,6 +14,7 @@ import { Parameter, Problem, Reference, + Return, Send, Singleton, Test, @@ -102,6 +104,8 @@ export const test = describe.members[0] as Test export const sentence = method.sentences()[0] +export const wReturn = new Return({ value: sentence as Expression }) + // PROBLEMS export const problem = (node: Node): Problem => ({ diff --git a/src/components/ui/Body/AssignmentFormModal.tsx b/src/components/Body/AssignmentFormModal.tsx similarity index 89% rename from src/components/ui/Body/AssignmentFormModal.tsx rename to src/components/Body/AssignmentFormModal.tsx index f541cf6..d61176d 100644 --- a/src/components/ui/Body/AssignmentFormModal.tsx +++ b/src/components/Body/AssignmentFormModal.tsx @@ -10,10 +10,10 @@ import { Reference, Variable, } from 'wollok-ts/dist/model' -import { wTranslate } from '../../../utils/translation-helpers' -import { Referenciable } from '../../../utils/wollok-helpers' -import ExpressionInput from '../ExpressionInput' -import FormModal from '../FormModal/FormModal' +import { wTranslate } from '../../utils/translation-helpers' +import { Referenciable } from '../../utils/wollok-helpers' +import ExpressionInput from '../ui/ExpressionInput' +import FormModal from '../ui/FormModal/FormModal' type AssignmentFormModalProps = { variables: Referenciable[] diff --git a/src/components/ui/Body/BodyMaker.tsx b/src/components/Body/BodyMaker.tsx similarity index 90% rename from src/components/ui/Body/BodyMaker.tsx rename to src/components/Body/BodyMaker.tsx index 50473a6..5afb1ed 100644 --- a/src/components/ui/Body/BodyMaker.tsx +++ b/src/components/Body/BodyMaker.tsx @@ -4,11 +4,11 @@ import { ScrollView, StyleSheet } from 'react-native' import { upperCaseFirst } from 'upper-case-first' import { Body, Expression, Name, Return, Sentence } from 'wollok-ts/dist/model' import { List } from 'wollok-ts/dist/extensions' -import { ExpressionOnSubmit } from '../../../pages/ExpressionMaker' -import { wTranslate } from '../../../utils/translation-helpers' -import { Referenciable } from '../../../utils/wollok-helpers' -import MultiFabScreen from '../../FabScreens/MultiFabScreen' -import { SubmitCheckButton } from '../Header' +import { ExpressionOnSubmit } from '../../pages/ExpressionMaker' +import { wTranslate } from '../../utils/translation-helpers' +import { Referenciable } from '../../utils/wollok-helpers' +import MultiFabScreen from '../FabScreens/MultiFabScreen' +import { SubmitCheckButton } from '../ui/Header' import { AssignmentFormModal } from './AssignmentFormModal' import { returnIcon as returnIconName } from './sentences/Return' import { VisualSentence } from './sentences/VisualSentence' diff --git a/src/components/ui/Body/VariableForm.tsx b/src/components/Body/VariableForm.tsx similarity index 83% rename from src/components/ui/Body/VariableForm.tsx rename to src/components/Body/VariableForm.tsx index eb90d88..9d3520d 100644 --- a/src/components/ui/Body/VariableForm.tsx +++ b/src/components/Body/VariableForm.tsx @@ -2,11 +2,11 @@ import React, { useState } from 'react' import { Text, TextInput } from 'react-native-paper' import { upperCaseFirst } from 'upper-case-first' import { Expression, Name, Variable } from 'wollok-ts/dist/model' -import { wTranslate } from '../../../utils/translation-helpers' -import CheckIcon from '../CheckIcon' -import ExpressionInput from '../ExpressionInput' -import FormModal from '../FormModal/FormModal' -import { Row } from '../Row' +import { wTranslate } from '../../utils/translation-helpers' +import CheckIcon from '../ui/CheckIcon' +import ExpressionInput from '../ui/ExpressionInput' +import FormModal from '../ui/FormModal/FormModal' +import { Row } from '../ui/Row' type VariableFormModalProps = { onSubmit: (assignment: Variable) => void @@ -49,7 +49,7 @@ export function VariableFormModal({ diff --git a/src/components/ui/Body/sentences/Assignment.tsx b/src/components/Body/sentences/Assignment.tsx similarity index 64% rename from src/components/ui/Body/sentences/Assignment.tsx rename to src/components/Body/sentences/Assignment.tsx index e005b0a..7fc2ada 100644 --- a/src/components/ui/Body/sentences/Assignment.tsx +++ b/src/components/Body/sentences/Assignment.tsx @@ -1,9 +1,9 @@ import React from 'react' import { Assignment as AssignmentModel } from 'wollok-ts/dist/model' -import { ReferenceSegment } from '../../../expressions/expression-segment' -import { display } from '../../../expressions/ExpressionDisplay' -import { ProblemReporterButton } from '../../../problems/ProblemReporterButton' -import { Row } from '../../Row' +import { ReferenceSegment } from '../../expressions/expression-segment' +import { display } from '../../expressions/ExpressionDisplay' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' +import { Row } from '../../ui/Row' import { ReferenceExpression } from './ReferenceExpression' export const Assignment = ({ assignment }: { assignment: AssignmentModel }) => { diff --git a/src/components/ui/Body/sentences/ReferenceExpression.tsx b/src/components/Body/sentences/ReferenceExpression.tsx similarity index 76% rename from src/components/ui/Body/sentences/ReferenceExpression.tsx rename to src/components/Body/sentences/ReferenceExpression.tsx index 1387e0f..67d20a6 100644 --- a/src/components/ui/Body/sentences/ReferenceExpression.tsx +++ b/src/components/Body/sentences/ReferenceExpression.tsx @@ -1,8 +1,8 @@ import React from 'react' import { IconButton } from 'react-native-paper' import { Expression } from 'wollok-ts/dist/model' -import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay' -import { Row } from '../../Row' +import { ExpressionDisplay } from '../../expressions/ExpressionDisplay' +import { Row } from '../../ui/Row' export const ReferenceExpression = (props: { expression: Expression }) => { return ( diff --git a/src/components/ui/Body/sentences/Return.tsx b/src/components/Body/sentences/Return.tsx similarity index 69% rename from src/components/ui/Body/sentences/Return.tsx rename to src/components/Body/sentences/Return.tsx index 480d72c..df5b6f3 100644 --- a/src/components/ui/Body/sentences/Return.tsx +++ b/src/components/Body/sentences/Return.tsx @@ -1,10 +1,10 @@ import React from 'react' import { IconButton } from 'react-native-paper' import { Return as ReturnModel } from 'wollok-ts/dist/model' -import { useTheme } from '../../../../theme' -import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay' -import { ProblemReporterButton } from '../../../problems/ProblemReporterButton' -import { Row } from '../../Row' +import { useTheme } from '../../../theme' +import { ExpressionDisplay } from '../../expressions/ExpressionDisplay' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' +import { Row } from '../../ui/Row' export const returnIcon = 'arrow-expand-up' diff --git a/src/components/ui/Body/sentences/Send.tsx b/src/components/Body/sentences/Send.tsx similarity index 59% rename from src/components/ui/Body/sentences/Send.tsx rename to src/components/Body/sentences/Send.tsx index a5b0395..034fe95 100644 --- a/src/components/ui/Body/sentences/Send.tsx +++ b/src/components/Body/sentences/Send.tsx @@ -1,8 +1,8 @@ import React from 'react' import { Send as SendModel } from 'wollok-ts/dist/model' -import { ExpressionDisplay } from '../../../expressions/ExpressionDisplay' -import { ProblemReporterButton } from '../../../problems/ProblemReporterButton' -import { Row } from '../../Row' +import { ExpressionDisplay } from '../../expressions/ExpressionDisplay' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' +import { Row } from '../../ui/Row' export const Send = ({ send }: { send: SendModel }) => { return ( diff --git a/src/components/ui/Body/sentences/Variable.tsx b/src/components/Body/sentences/Variable.tsx similarity index 69% rename from src/components/ui/Body/sentences/Variable.tsx rename to src/components/Body/sentences/Variable.tsx index edb8a34..094a747 100644 --- a/src/components/ui/Body/sentences/Variable.tsx +++ b/src/components/Body/sentences/Variable.tsx @@ -1,11 +1,11 @@ import React from 'react' import { IconButton, Text } from 'react-native-paper' import { Variable as VariableModel } from 'wollok-ts/dist/model' -import { useTheme } from '../../../../theme' -import { isNullExpression } from '../../../../utils/wollok-helpers' -import { ProblemReporterButton } from '../../../problems/ProblemReporterButton' -import { ConstantVariableIcon } from '../../ConstantVariableIcon' -import { Row } from '../../Row' +import { useTheme } from '../../../theme' +import { isNullExpression } from '../../../utils/wollok-helpers' +import { ProblemReporterButton } from '../../problems/ProblemReporterButton' +import { ConstantVariableIcon } from '../../ui/ConstantVariableIcon' +import { Row } from '../../ui/Row' import { ReferenceExpression } from './ReferenceExpression' export const Variable = (props: { variable: VariableModel }) => { diff --git a/src/components/ui/Body/sentences/VisualSentence.tsx b/src/components/Body/sentences/VisualSentence.tsx similarity index 88% rename from src/components/ui/Body/sentences/VisualSentence.tsx rename to src/components/Body/sentences/VisualSentence.tsx index 784ce75..b88be21 100644 --- a/src/components/ui/Body/sentences/VisualSentence.tsx +++ b/src/components/Body/sentences/VisualSentence.tsx @@ -1,8 +1,8 @@ import React from 'react' import { Text } from 'react-native-paper' import { Sentence } from 'wollok-ts/dist/model' -import { wTranslate } from '../../../../utils/translation-helpers' -import { Row } from '../../Row' +import { wTranslate } from '../../../utils/translation-helpers' +import { Row } from '../../ui/Row' import { Assignment } from './Assignment' import { Return } from './Return' import { Send } from './Send' diff --git a/src/components/problems/ProblemReporterButton.tsx b/src/components/problems/ProblemReporterButton.tsx index 0234e13..1bd1474 100644 --- a/src/components/problems/ProblemReporterButton.tsx +++ b/src/components/problems/ProblemReporterButton.tsx @@ -21,6 +21,7 @@ export function ProblemReporterButton({ node.match({ Method: m => problem.node.ancestors().includes(m), Test: t => problem.node.ancestors().includes(t), + Return: r => r.id === problem.node.id || r.value?.id === problem.node.id, Node: n => n.id === problem.node.id, }) diff --git a/src/pages/Editor.tsx b/src/pages/Editor.tsx index d9b67d8..43ee6eb 100644 --- a/src/pages/Editor.tsx +++ b/src/pages/Editor.tsx @@ -2,7 +2,7 @@ import { RouteProp, useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' import React from 'react' import { Body } from 'wollok-ts/dist/model' -import { BodyMaker } from '../components/ui/Body/BodyMaker' +import { BodyMaker } from '../components/Body/BodyMaker' import { useProject } from '../context/ProjectProvider' import { allScopedVariables, diff --git a/src/utils/test-helpers.tsx b/src/utils/test-helpers.tsx deleted file mode 100644 index fb9afa8..0000000 --- a/src/utils/test-helpers.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { NavigationContainer } from '@react-navigation/native' -import { render } from '@testing-library/react-native' -import React from 'react' -import { theme } from '../theme' -import { OneOrMany } from './type-helpers' - -export function renderWithTheme(children: OneOrMany) { - return render( - {children}, - ) -}