Skip to content

Commit

Permalink
fix: reset code to worksheet's default (#1445)
Browse files Browse the repository at this point in the history
* fix: reset code to worksheet's default

added a confirm dialog as well

* test reset code with confirmation
  • Loading branch information
razvan-pro committed Jan 18, 2021
1 parent 60467bf commit 2ce3a68
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 84 deletions.
5 changes: 4 additions & 1 deletion aimmo/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,10 @@ def test_retrieve_code(self):
c = self.login()
response = c.get(reverse("kurono/code", kwargs={"id": 1}))
self.assertEqual(response.status_code, 200)
self.assertJSONEqual(response.content, {"code": self.CODE})
self.assertJSONEqual(
response.content,
{"code": self.CODE, "starterCode": self.game.worksheet.starter_code},
)

def _associate_game_as_level_num(self, level_num=1, user=None, game=None):
if game is None:
Expand Down
4 changes: 3 additions & 1 deletion aimmo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def code(request, id):
avatar.save()
return HttpResponse(status=200)
else:
return JsonResponse({"code": avatar.code})
return JsonResponse(
{"code": avatar.code, "starterCode": game.worksheet.starter_code}
)


class GameUsersView(APIView):
Expand Down
30 changes: 15 additions & 15 deletions game_frontend/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
module.exports = {
parser: '@typescript-eslint/parser',
extends: ['standard', 'standard-react', 'plugin:cypress/recommended'],
extends: ['standard', 'standard-react', 'plugin:cypress/recommended', 'prettier'],
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module'
sourceType: 'module',
},
env: {
browser: true,
'cypress/globals': true
'cypress/globals': true,
},
globals: {
pyodide: 'readonly',
languagePluginLoader: 'readonly'
languagePluginLoader: 'readonly',
},
rules: {
'react/jsx-handler-names': 'off',
'react/jsx-pascal-case': [
'error',
{
allowAllCaps: true
}
allowAllCaps: true,
},
],
'@typescript-eslint/member-delimiter-style': [
'error',
{
multiline: {
delimiter: 'none'
}
}
delimiter: 'none',
},
},
],
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'none'
}
args: 'none',
},
],
'no-console': [
'error',
{
allow: ['warn', 'error']
}
]
}
allow: ['warn', 'error'],
},
],
},
}
33 changes: 33 additions & 0 deletions game_frontend/cypress/integration/user/actions.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const avatarCode = {
code: `current code`,
starterCode: `starter code`,
}

describe('User', () => {
before(() => {
cy.login()
cy.addTestGame()
})

beforeEach(() => {
cy.login()
})

it('resets their code to the default one in the Worksheet if they click on Reset Code and confirm', () => {
cy.loadGameWithAvatarCode(avatarCode)
cy.on('window:confirm', () => true)
cy.get('button').contains('Reset code').click()
cy.window()
.then((win) => win.ace.edit('ace_editor').getValue())
.should('eq', avatarCode.starterCode)
})

it('does not reset their code to the default one in the Worksheet if they click on Reset Code and not confirm', () => {
cy.loadGameWithAvatarCode(avatarCode)
cy.on('window:confirm', () => false)
cy.get('button').contains('Reset code').click()
cy.window()
.then((win) => win.ace.edit('ace_editor').getValue())
.should('eq', avatarCode.code)
})
})
4 changes: 2 additions & 2 deletions game_frontend/cypress/integration/user/navigation.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/// <reference types="cypress" />

testLogoClickNavigatesToUrl = (url) => {
function testLogoClickNavigatesToUrl(url) {
cy.get('nav > header a[aria-label="Kurono dashboard"]').click()
cy.url().should('eq', url)
}

testExitButtonClickNavigatesToUrl = (url) => {
function testExitButtonClickNavigatesToUrl(url) {
cy.get('nav > header').contains('Exit game').click()
cy.url().should('eq', url)
}
Expand Down
5 changes: 3 additions & 2 deletions game_frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
"enzyme-adapter-react-16": "^1.14.0",
"enzyme-to-json": "^3.3.5",
"eslint": "^6.8.0",
"eslint-config-prettier": "^7.1.0",
"eslint-config-standard": "^14.1.0",
"eslint-config-standard-react": "^9.2.0",
"eslint-plugin-cypress": "^2.11.2",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
Expand All @@ -56,7 +58,6 @@
"babylon": "^6.18.0",
"babylonjs": "^4.1.0",
"core-js": "3",
"eslint-plugin-cypress": "^2.10.3",
"handlebars": "^4.7.6",
"immer": "^7.0.1",
"js-cookie": "^2.2.1",
Expand Down Expand Up @@ -92,4 +93,4 @@
"lint": "eslint 'src/**/*.{js,ts}'",
"test": "jest"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`<IDEConsole /> renders correctly 1`] = `
<styled.section>
<ConsoleBar
clearConsoleClicked={[Function]}
handleResetCodeClicked={[Function]}
/>
<styled.div>
<LogEntries
Expand Down
46 changes: 26 additions & 20 deletions game_frontend/src/containers/IDEConsole/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@ export const IDEConsoleSection = styled.section`
`

export const StyledConsole = styled.div`
color: ${props => props.theme.palette.text.primary};
background-color: ${props => props.theme.palette.background.default};
font-family: ${props => props.theme.additionalVariables.typography.code.fontFamily};
color: ${(props) => props.theme.palette.text.primary};
background-color: ${(props) => props.theme.palette.background.default};
font-family: ${(props) => props.theme.additionalVariables.typography.code.fontFamily};
overflow-y: auto;
height: 100%;
::-webkit-scrollbar {
background-color: ${props => props.theme.palette.divider};
background-color: ${(props) => props.theme.palette.divider};
}
::-webkit-scrollbar-track {
background: ${props => props.theme.palette.divider};
background: ${(props) => props.theme.palette.divider};
}
::-webkit-scrollbar-thumb {
background: ${props => props.theme.palette.grey.A200};
background: ${(props) => props.theme.palette.grey.A200};
border-radius: 100px;
border: ${props => props.theme.spacing(0.25)}px solid transparent;
border: ${(props) => props.theme.spacing(0.25)}px solid transparent;
background-clip: content-box;
}
`
Expand All @@ -40,35 +40,35 @@ export class IDEConsole extends Component {
static propTypes = {
logs: PropTypes.instanceOf(Map),
resetCode: PropTypes.func,
clearConsoleLogs: PropTypes.func
clearConsoleLogs: PropTypes.func,
}

// see https://blog.eqrion.net/pin-to-bottom/
state = {
activatedScrollToBottom: false,
shouldActivateSnapToBottom: false
shouldActivateSnapToBottom: false,
}

componentDidUpdate () {
componentDidUpdate() {
this.snapToBottomIfNeeded()
}

isOverflown ({ clientHeight, scrollHeight }) {
isOverflown({ clientHeight, scrollHeight }) {
return scrollHeight > clientHeight
}

isOverflownForTheFirstTime () {
isOverflownForTheFirstTime() {
return (
!this.state.activatedScrollToBottom && this.consoleRef && this.isOverflown(this.consoleRef)
)
}

snapToBottomIfNeeded () {
snapToBottomIfNeeded() {
if (this.isOverflownForTheFirstTime()) {
this.setState({
...this.state,
shouldActivateSnapToBottom: true,
activatedScrollToBottom: true
activatedScrollToBottom: true,
})
} else if (this.state.activatedScrollToBottom && this.state.shouldActivateSnapToBottom) {
this.setState({ ...this.state, shouldActivateSnapToBottom: false })
Expand All @@ -80,15 +80,21 @@ export class IDEConsole extends Component {
this.setState({ ...this.state, activatedScrollToBottom: false })
}

render () {
resetCode = () => {
if (confirm('Are you sure you want to reset to the starter code?')) {
this.props.resetCode()
}
}

render() {
return (
<IDEConsoleSection>
<ConsoleBar
clearConsoleClicked={this.clearConsole}
handleResetCodeClicked={this.props.resetCode}
handleResetCodeClicked={this.resetCode}
/>
<StyledConsole
ref={ref => {
ref={(ref) => {
this.consoleRef = ref
}}
>
Expand All @@ -102,13 +108,13 @@ export class IDEConsole extends Component {
}
}

const mapStateToProps = state => ({
logs: state.consoleLog.logs
const mapStateToProps = (state) => ({
logs: state.consoleLog.logs,
})

const mapDispatchToProps = {
clearConsoleLogs: actions.clearConsoleLogs,
resetCode: editorActions.resetCode
resetCode: editorActions.resetCode,
}

export default connect(mapStateToProps, mapDispatchToProps)(IDEConsole)
25 changes: 13 additions & 12 deletions game_frontend/src/redux/features/Editor/actions.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import types from './types'

const getCodeRequest = () => ({
type: types.GET_CODE_REQUEST
type: types.GET_CODE_REQUEST,
})

const getCodeReceived = code => ({
const getCodeReceived = (code, starterCode) => ({
type: types.GET_CODE_SUCCESS,
payload: {
code
}
code,
starterCode,
},
})

const postCodeRequest = code => ({
const postCodeRequest = (code) => ({
type: types.POST_CODE_REQUEST,
payload: {
code
}
code,
},
})

const postCodeReceived = () => ({
type: types.POST_CODE_SUCCESS
type: types.POST_CODE_SUCCESS,
})

const avatarCodeUpdated = () => ({
type: types.AVATAR_CODE_UPDATED
type: types.AVATAR_CODE_UPDATED,
})

const resetCode = () => ({
type: types.RESET_CODE
type: types.RESET_CODE,
})

const codeReset = () => ({
type: types.CODE_RESET
type: types.CODE_RESET,
})

export default {
Expand All @@ -41,5 +42,5 @@ export default {
postCodeReceived,
avatarCodeUpdated,
resetCode,
codeReset
codeReset,
}
22 changes: 11 additions & 11 deletions game_frontend/src/redux/features/Editor/epics.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import { ofType } from 'redux-observable'
const getCodeEpic = (action$, state$, { api }) =>
action$.pipe(
ofType(types.GET_CODE_REQUEST),
mergeMap(action =>
mergeMap((action) =>
api.get(`code/${state$.value.game.connectionParameters.game_id}/`).pipe(
map(response => actions.getCodeReceived(response.code)),
catchError(error =>
map((response) => actions.getCodeReceived(response.code, response.starterCode)),
catchError((error) =>
of({
type: types.GET_CODE_FAILURE,
payload: error.xhr.response,
error: true
error: true,
})
)
)
Expand All @@ -25,26 +25,26 @@ const getCodeEpic = (action$, state$, { api }) =>
const postCodeEpic = (action$, state$, { api }) =>
action$.pipe(
ofType(types.POST_CODE_REQUEST),
api.post(`/kurono/api/code/${state$.value.game.connectionParameters.game_id}/`, action => ({
code: action.payload.code
api.post(`/kurono/api/code/${state$.value.game.connectionParameters.game_id}/`, (action) => ({
code: action.payload.code,
})),
map(() => actions.postCodeReceived()),
catchError(error =>
catchError((error) =>
of({
type: types.POST_CODE_FAILURE,
payload: error.xhr.response,
error: true
error: true,
})
)
)

const postCodeAnalyticsEpic = action$ =>
const postCodeAnalyticsEpic = (action$) =>
action$.pipe(
ofType(types.POST_CODE_REQUEST),
mapTo(analyticActions.sendAnalyticsEvent('Kurono', 'Click', 'Run Code'))
)

const resetCodeAnalyticsEpic = action$ =>
const resetCodeAnalyticsEpic = (action$) =>
action$.pipe(
ofType(types.RESET_CODE),
mapTo(analyticActions.sendAnalyticsEvent('Kurono', 'Click', 'Reset Code'))
Expand All @@ -54,5 +54,5 @@ export default {
getCodeEpic,
postCodeEpic,
postCodeAnalyticsEpic,
resetCodeAnalyticsEpic
resetCodeAnalyticsEpic,
}
Loading

0 comments on commit 2ce3a68

Please sign in to comment.