Skip to content

Commit

Permalink
Update docs, scripts, and add two project settings tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MattyMay committed Oct 1, 2024
1 parent 3938079 commit d22e69c
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 35 deletions.
40 changes: 34 additions & 6 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ on:
branches: [ develop ]

jobs:
build:
lint:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v2
- run: npm run lint

component-tests:
runs-on: ubuntu-latest

strategy:
Expand All @@ -21,17 +32,34 @@ jobs:

steps:
- uses: actions/checkout@v2

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm install
- run: npm run lint
- run: npm run build
- run: npm test
- run: npx cypress run --component
- run: npm run component

# Starts servers and runs e2e tests once the website is available
- run: npm run e2e
e2e-tests:
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v2
with:
submodules: 'true'

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- run: npm run e2e
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,58 @@ For information about contributing to Autograder.io, see our
Follow the [dev stack setup tutorial](https://github.com/eecs-autograder/autograder-full-stack/blob/master/docs/development_setup.md) for the [autograder-full-stack repo](https://github.com/eecs-autograder/autograder-full-stack).

## Dev commands
The unit tests currently support Node.js version 16 (newer versions may work too).
### Run all tests
To run all tests, Node.js version 16 is **required**. Newer versions will not work.
You can install this version with [NVM](https://github.com/nvm-sh/nvm/blob/master/README.md)
by running:
```
nvm install 16
```

To run the unit tests (with coverage):
To run both component tests and e2e tests:
```
npm test
```

### Unit tests
The unit tests currently **require** Node.js version 16. Newer versions will not work.
See above about installing this version.

To run the unit tests (with coverage):
```
npm run component
```

### e2e tests
The e2e tests currently require at least Node.js version 16. Newer versions may work.
See above for installing this version.

To run the e2e tests with a headless browser:
```
npm run e2e
```

To open a full browser from which you can run e2e tests:
```
npm run e2e:browser
```

Both of these npm scripts will start a backend server for the website to interact with and
shut it down once the tests are complete. If you don't want to wait for the app to compile
each time you run the tests, you can alternatively start the backend manually and leave
it running while you run the tests. If you do this, changes to the source code will automatically
cause the app to recompile but it will be much faster than compiling everything from scratch.
```
# start backend
npm run e2e:serve
# in a different terminal, or if you ran the serve script as a background process...
npm run e2e:cy:run
# or to open in a browser...
npm run e2e:cy:open
```

## Coding Standards
In addition to the items listed here, all source code must follow our
[Typescript/Vue coding standards](https://github.com/eecs-autograder/autograder.io/blob/master/coding_standards_typescript_vue.md).
Expand Down
34 changes: 25 additions & 9 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { defineConfig } from "cypress";
import * as child_process from 'child_process';

// reset db
// make super user
const SUPERUSER_NAME = '[email protected]'
const ADMIN_NAME = '[email protected]'
const STAFF_NAME = '[email protected]'
const STUDENT_NAME = '[email protected]'

const CONTAINER_NAME = 'ag-vue-e2e-django'

export default defineConfig({
component: {
Expand All @@ -18,13 +22,16 @@ export default defineConfig({
setup_db
})
},
baseUrl: 'http://localhost:8080'
baseUrl: 'http://localhost:8080',
env: {
superuser: SUPERUSER_NAME,
admin: ADMIN_NAME,
staff: STAFF_NAME,
student: STUDENT_NAME,
},
},
});

const SUPERUSER_NAME = '[email protected]'
const CONTAINER_NAME = 'ag-vue-e2e-django'

const setup_db = () => {
let django_code = `import shutil
from django.core.cache import cache;
Expand All @@ -38,9 +45,18 @@ BuildSandboxDockerImageTask.objects.all().delete()
shutil.rmtree('/usr/src/app/media_root_dev/', ignore_errors=True)
cache.clear()
user = User.objects.get_or_create(username='${SUPERUSER_NAME}')[0]
user.is_superuser = True
user.save()
superuser = User.objects.get_or_create(username='${SUPERUSER_NAME}')[0]
superuser.is_superuser = True
superuser.save()
admin = User.objects.get_or_create(username='${ADMIN_NAME}')[0]
admin.save()
staff = User.objects.get_or_create(username='${STAFF_NAME}')[0]
staff.save()
student = User.objects.get_or_create(username='${STUDENT_NAME}')[0]
student.save()
`;
return run_in_django_shell(django_code);
}
Expand Down
122 changes: 122 additions & 0 deletions cypress/e2e/admin/project_settings.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
const username = Cypress.env('admin')
const course_name = 'Nerdy Algos'
const project_name = 'TSP'

const build_full_url = (uri: string): string => {
return Cypress.config().baseUrl + uri
}

describe('project settings page as admin', () => {
beforeEach(() => {
cy.task('setup_db')
.create_course(course_name)
.as('course_pk', { type: 'static' });

cy.fake_login(username)
.get('@course_pk').then((course_pk) => {
cy.create_project(Number(course_pk), project_name).as('project_pk', { type: 'static' });
});

cy.get('@project_pk').then(pk => {
cy.wrap(`/web/project_admin/${pk}`).as('page_uri')
});
})

it('has functioning navbar tabs', function() {
cy.visit(this.page_uri)
.get_by_testid('settings-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=settings`))

cy.visit(this.page_uri)
.get_by_testid('instructor-files-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=instructor_files`))

cy.visit(this.page_uri)
.get_by_testid('instructor-files-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=instructor_files`))

cy.visit(this.page_uri)
.get_by_testid('student-files-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=expected_student_files`))

cy.visit(this.page_uri)
.get_by_testid('test-cases-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=test_cases`))

cy.visit(this.page_uri)
.get_by_testid('mutation-testing-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=mutation_testing`))

cy.visit(this.page_uri)
.get_by_testid('edit-groups-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=edit_groups`))

cy.visit(this.page_uri)
.get_by_testid('download-grades-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=download_grades`))

cy.visit(this.page_uri)
.get_by_testid('rerun-tests-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=rerun_tests`))

cy.visit(this.page_uri)
.get_by_testid('handgrading-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=handgrading`))

cy.visit(this.page_uri)
.get_by_testid('stats-tab')
.should('be.visible').click()
.url().should('eq', build_full_url(`${this.page_uri}?current_tab=stats`))
});

it('allows user to set and delete soft deadline', function() {
// April 3, 2024 12:01 PM
const now = new Date(2024, 3, 14, 12, 1)
cy.clock(now)

const new_date = '15'
const new_hours = '2'
const new_minutes = '30'

// See FIXME below...
const new_datetime_str = 'April 15, 2024, 02:30 PM PDT'

cy.visit(this.page_uri)
.get_by_testid('soft-deadline-input').should('be.visible').click();

cy.get_by_testid('soft-deadline-picker').should('be.visible').find('.available-day')
.contains(new_date).should('be.visible').click();

cy.get_by_testid('period-input').should('be.visible').should('have.value', 'PM').click();

cy.get_by_testid('hour-input').should('be.visible').should('have.value', '12')
.type(new_hours);

cy.get_by_testid('minute-input').should('be.visible').should('have.value', '01')
.type(new_minutes);

// TZ environment variable is set within the npm script
cy.get_by_testid('timezone-select').should('have.value', 'America/Los_Angeles');

// FIXME?: It feels weird that the period automatically switches to AM when typing a new value
cy.get_by_testid('period-input').should('be.visible').should('have.value', 'AM').click();

// check date has been updated on page
cy.get_by_testid('soft-deadline-input').should('contain.text', new_datetime_str)

// save amd check that new data is loaded on refresh
cy.get_by_testid('save-button').should('be.visible').click()

cy.reload().get_by_testid('soft-deadline-input').should('contain.text', new_datetime_str)
})
})
13 changes: 12 additions & 1 deletion cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,15 @@
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
// }
//

declare namespace Cypress {
interface Chainable {
get_by_testid(selector: string, ...args): Chainable
}
}

Cypress.Commands.add('get_by_testid', (selector, ...args) => {
return cy.get(`[data-testid=${selector}]`, ...args)
})
68 changes: 65 additions & 3 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,70 @@
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
const { superuser, admin, staff, student } = Cypress.env()

declare global {
namespace Cypress {
interface Chainable {
create_course(course_name: string): Chainable
create_project(course_pk: number, project_name: string): Chainable
fake_login(username: string): Chainable
logout(): Chainable
}
}
}

Cypress.Commands.add('fake_login', username => {
cy.setCookie('token', 'foo').setCookie('username', username)
})

Cypress.Commands.add('logout', () => {
cy.clearAllCookies()
})

/*
* Create a course by making a POST request to the API and yield the pk
*/
Cypress.Commands.add('create_course', course_name => {
cy.fake_login(superuser)

cy.request('POST', '/api/courses/', {
'name': course_name,
'semester': 'Fall',
'year': 2024,
'subtitle': 'This is a subtitle',
'num_late_days': 0,
'allowed_guest_domain': 'autograder.io'
})
.then((res) => cy.wrap(res.body.pk))
.then((pk) => {
cy.request('POST', `/api/courses/${pk}/admins/`, {
'new_admins': [ admin ]
});
return cy.wrap(pk);
})
.then((pk) => {
cy.request('POST', `/api/courses/${pk}/staff/`, {
'new_staff': [ staff ]
});
return cy.wrap(pk);
})
.then((pk) => {
cy.request('POST', `/api/courses/${pk}/students/`, {
'new_students': [ student ]
});
cy.logout();
return cy.wrap(pk);
})
})

/*
* Create a project by making a POST request to the API and yield the pk
*/
Cypress.Commands.add('create_project', (course_pk, project_name) => {
cy.request('POST', `/api/courses/${course_pk}/projects/`, {
'name': project_name
}).then((res) => cy.wrap(res.body.pk))
})
Loading

0 comments on commit d22e69c

Please sign in to comment.