Merge branch 'master' into production

This commit is contained in:
phoenixeliot 2016-08-01 16:46:33 -07:00
commit fc78c5af82
12 changed files with 140 additions and 82 deletions

View file

@ -1351,6 +1351,7 @@
update_account_students_warning: "Warning: You will not be able to manage any classes that you have previously created or create new classes."
update_account_remain_student: "Remain a Student"
update_account_update_student: "Update to Student"
need_a_class_code: "You'll need a Class Code for the class you're joining:"
update_account_not_sure: "Not sure which one to choose? Email"
update_account_confirm_update_student: "Are you sure you want to update your account to a Student experience?"
update_account_confirm_update_student2: "You will not be able to manage any classes that you have previously created or create new classes. Your previously created classes will be removed from CodeCombat and cannot be restored."

View file

@ -65,13 +65,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# setup_a_class: "Set Up a Class"
have_an_account: "Tens uma conta?"
logged_in_as: "Atualmente tens sessão iniciada como"
# view_my_classes: "View my classes"
view_my_classes: "Ver as minhas turmas"
computer_science: "Cursos de ciência da computação para todas as idades"
show_me_lesson_time: "Mostrar estimativas do tempo de aula para:"
curriculum: "Horas totais do currículo:"
ffa: "Grátis para todos os estudantes"
lesson_time: "Tempo de aula:"
coming_soon: "Brevemente!"
coming_soon: "Chega neste outono!"
courses_available_in: "Os cursos estão disponíveis em JavaScript, Python, e Java (brevemente!)"
# boast: "Boasts riddles that are complex enough to fascinate gamers and coders alike."
# winning: "A winning combination of RPG gameplay and programming homework that pulls off making kid-friendly education legitimately enjoyable."
@ -86,8 +86,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# student_count: "Number of students:"
# start_playing_for_free: "Start Playing for Free!"
# students_and_players: "Students & Players"
# goto_classes: "Go to My Classes"
# view_profile: "View My Profile"
goto_classes: "Ir para As Minhas Turmas"
view_profile: "Ver o Meu Perfil"
view_progress: "Ver Progresso"
# check_out_wiki: "Check out our new Educator Wiki"
# want_coco: "Want CodeCombat at your school?"
@ -132,7 +132,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
help_suff: "e nós entraremos em contacto!"
modal:
# cancel: "Cancel"
cancel: "Cancelar"
close: "Fechar"
okay: "Ok"
@ -243,7 +243,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
login:
sign_up: "Criar Conta"
# email_or_username: "Email or username"
email_or_username: "E-mail ou nome de utilizador"
log_in: "Iniciar Sessão"
logging_in: "A Iniciar Sessão"
log_out: "Sair"
@ -256,13 +256,13 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
signup_switch: "Queres criar uma conta?"
signup:
# create_student_header: "Create Student Account"
# create_teacher_header: "Create Teacher Account"
# create_individual_header: "Create Individual Account"
# create_header: "Create Account"
email_announcements: "Receber anúncios por e-mail" # {change}
create_student_header: "Criar Conta de Estudante"
create_teacher_header: "Criar Conta de Professor"
create_individual_header: "Criar Conta Individual"
create_header: "Criar Conta"
email_announcements: "Receber anúncios sobre níveis e funcionalidades novos do CodeCombat!"
creating: "A Criar Conta..."
# create_account: "Create Account"
create_account: "Criar Conta"
sign_up: "Registar"
log_in: "iniciar sessão com palavra-passe"
required: "Precisas de iniciar sessão antes de prosseguires."
@ -270,7 +270,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
school_name: "Nome e Cidade da Escola"
optional: "opcional"
school_name_placeholder: "Exemplo: Escola Secundária, Springfield, Illinois"
# connect_with: "Connect with:"
connect_with: "Conectar com:"
# connected_gplus_header: "You've successfully connected with Google+!"
# connected_gplus_p: "Finish signing up so you can log in with your Google+ account."
# gplus_exists: "You already have an account associated with Google+!"
@ -340,8 +340,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
saving: "A Guardar..."
sending: "A Enviar..."
send: "Enviar"
# sent: "Sent"
# type: "Type"
sent: "Enviado"
type: "Tipo"
cancel: "Cancelar"
save: "Guardar"
publish: "Publicar"
@ -411,7 +411,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
wizard: "Feiticeiro"
first_name: "Nome"
last_name: "Apelido"
# last_initial: "Last Initial"
last_initial: "Última Inicial"
username: "Nome de utilizador"
units:
@ -480,12 +480,12 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tome_cast_button_running: "A Executar"
tome_cast_button_ran: "Executado"
tome_submit_button: "Submeter"
tome_reload_method: "Recarregar o código original para este método" # {change}
tome_reload_method: "Recarregar o código original para recomeçar o nível"
tome_available_spells: "Feitiços Disponíveis"
tome_your_skills: "As Tuas Habilidades"
tome_current_method: "Método Atual"
# hints: "Hints"
# hints_title: "Hint {{number}}"
hints: "Dicas"
hints_title: "Dica {{number}}"
code_saved: "Código Guardado"
skip_tutorial: "Saltar (esc)"
keyboard_shortcuts: "Atalhos de Teclado"
@ -799,8 +799,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
phoenix_title: "Engenheira de 'Software'"
nolan_title: "Gestor de Território"
elliot_title: "Gestor de Parcerias"
# elliot_blurb: "Mindreader"
# lisa_title: "Market Development Rep"
elliot_blurb: "Leitora de Mentes"
lisa_title: "Representante do Desenvolvimento de Mercado"
retrostyle_title: "Ilustração"
retrostyle_blurb: "'RetroStyle Games'"
jose_title: "Música"
@ -861,7 +861,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
more_info_1: "O nosso"
more_info_2: "fórum para professores"
more_info_3: "é um bom sítio para te ligares a outros professores que também usam o CodeCombat."
# licenses_needed: "Licenses needed"
licenses_needed: "Licenças necessárias"
# teachers_quote:
# name: "Demo Form"
@ -960,7 +960,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
manage_subscription: "Clica aqui para gerires a tua subscrição."
new_password: "Nova Palavra-passe"
new_password_verify: "Verificar"
type_in_email: "Escreve o teu e-mail para confirmares a eliminação." # {change}
type_in_email: "Escreve o teu e-mail ou nome de utilizador para confirmares a eliminação da conta."
type_in_email_progress: "Escreve o teu e-mail para confirmares a eliminação do teu progresso."
type_in_password: "Escreve também a tua palavra-passe."
email_subscriptions: "Subscrições de E-mail"
@ -1161,9 +1161,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
get_free: "Obter curso GRÁTIS"
enroll_paid: "Inscrever Estudantes em Cursos Pagos"
you_have1: "Tens"
you_have2: "inscrições pagas por usar" # {change}
# use_one: "Use 1 student license for"
# use_multiple: "Use licenses for the following students:"
you_have2: "licenças de estudante por usar"
use_one: "Usar 1 licença de estudante para"
use_multiple: "Usar licenças para os seguintes estudantes:"
# already_enrolled: "already enrolled"
# licenses_remaining: "licenses remaining:"
# insufficient_enrollments: "insufficient student licenses"
@ -1177,11 +1177,11 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# edit_details: "Edit class details"
# enrolled_courses: "enrolled in paid courses:"
# purchase_enrollments: "Purchase Student Licenses"
# remove_student: "remove student"
remove_student: "remover estudante"
# assign: "Assign"
# to_assign: "to assign paid courses."
# student: "Student"
# teacher: "Teacher"
student: "Estudante"
teacher: "Professor"
# complete: "Complete"
# none: "None"
# save: "Save"
@ -1216,10 +1216,10 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# ready_for_more_3: "Learn even more programming!"
# saved_games: "Saved Games"
# hoc: "Hour of Code"
# my_classes: "My Classes"
my_classes: "As Minhas Turmas"
# class_added: "Class successfully added!"
# view_class: "view class"
# view_levels: "view levels"
view_class: "ver turma"
view_levels: "ver níveis"
# join_class: "Join A Class"
# join_class_2: "Join class"
# ask_teacher_for_code: "Ask your teacher if you have a CodeCombat class code! If so, enter it below:"
@ -1235,7 +1235,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
not_you: "Não és tu?"
# welcome_back: "Hi adventurer, welcome back!"
continue_playing: "Continuar a Jogar"
# more_options: "More options:"
more_options: "Mais opções:"
# option1_header: "Option 1: Invite students via email"
# option1_body: "Students will automatically be sent an invitation to join this class, and will need to create an account with a username and password."
# option2_header: "Option 2: Send URL to your students"
@ -1252,18 +1252,18 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# each_student_access: "Each student in a class will get access to Courses 2-4 once they are enrolled in paid courses. You may assign each course to each student individually."
# purchase_now: "Purchase Now"
# enrollments: "licenses"
# remove_student1: "Remove Student"
remove_student1: "Remover Estudante"
# are_you_sure: "Are you sure you want to remove this student from this class?"
# remove_description1: "Student will lose access to this classroom and assigned classes. Progress and gameplay is NOT lost, and the student can be added back to the classroom at any time."
# remove_description2: "The activated paid license will not be returned."
# keep_student: "Keep Student"
# removing_user: "Removing user"
removing_user: "A remover utilizador"
# to_join_ask: "To join a class, ask your teacher for an unlock code."
# join_this_class: "Join Class"
# enter_here: "<enter unlock code here>"
# successfully_joined: "Successfully joined"
# click_to_start: "Click here to start taking"
# my_courses: "My Courses"
my_courses: "Os Meus Cursos"
# classroom: "Classroom"
# use_school_email: "use your school email if you have one"
# unique_name: "a unique name no one has chosen"
@ -1295,12 +1295,12 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# students_enrolled: "students enrolled"
# students_assigned: "students assigned"
# length: "Length:"
title: "Título" # Flat style redesign
title: "Cursos" # Flat style redesign
# subtitle: "Review course guidelines, solutions, and levels"
# changelog: "View latest changes to course levels."
# select_language: "Select language"
# select_level: "Select level"
# play_level: "Play Level"
select_level: "Selecionar nível"
play_level: "Jogar Nível"
concepts_covered: "Conceitos Abordados"
# print_guide: "Print Guide (PDF)"
# view_guide_online: "View Guide Online (PDF)"
@ -1369,26 +1369,26 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# select_your_hero_description: "You can always change your hero by going to your Courses page and clicking \"Select Hero\""
# select_this_hero: "Select this Hero"
# teacher:
teacher:
# teacher_dashboard: "Teacher Dashboard" # Navbar
# my_classes: "My Classes"
my_classes: "As Minhas Turmas"
# courses: "Course Guides"
# enrollments: "Student Licenses"
# resources: "Resources"
# help: "Help"
# students: "Students" # Shared
# language: "Language"
resources: "Recursos"
help: "Ajuda"
students: "Estudantes" # Shared
language: "Linguagem"
# edit_class_settings: "edit class settings"
# complete: "Complete"
# access_restricted: "Account Update Required"
# teacher_account_required: "A teacher account is required to access this content."
# create_teacher_account: "Create Teacher Account"
# what_is_a_teacher_account: "What's a Teacher Account?"
create_teacher_account: "Criar Conta de Professor"
what_is_a_teacher_account: "O que é uma Conta de Professor?"
# teacher_account_explanation: "A CodeCombat Teacher account allows you to set up classrooms, monitor students progress as they work through courses, manage licenses and access resources to aid in your curriculum-building."
# current_classes: "Current Classes"
# archived_classes: "Archived Classes"
current_classes: "Turmas Atuais"
archived_classes: "Turmas Arquivadas"
# archived_classes_blurb: "Classes can be archived for future reference. Unarchive a class to view it in the Current Classes list again."
# view_class: "view class"
view_class: "ver turma"
# archive_class: "archive class"
# unarchive_class: "unarchive class"
# unarchive_this_class: "Unarchive this class"
@ -1494,8 +1494,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# victory_course_share_suffix: "you just created."
# copy_url: "Copy URL"
# game_dev:
# creator: "Creator"
game_dev:
creator: "Criador"
# web_dev:
# image_gallery_title: "Image Gallery"
@ -1657,7 +1657,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# scribe_introduction_url_mozilla: "Mozilla Developer Network"
# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you."
scribe_attribute_1: "Habilidade com palavras é basicamente o que precisas. Não apenas gramática e ortografia, mas seres capaz de explicar ideias complicadas a outros."
contact_us_url: "Contacta-nos" # {change}
contact_us_url: "Contacta-nos"
scribe_join_description: "fala-nos um bocado de ti, da tua experiência com a programação e do tipo de coisas sobre as quais gostarias de escrever. Começamos a partir daí!"
scribe_subscribe_desc: "Receber e-mails sobre anúncios relativos à escrita de artigos."
diplomat_introduction_pref: "Portanto, se há uma coisa que aprendemos com o nosso "
@ -1800,15 +1800,15 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
card: "Cartão"
status_unsubscribed_active: "Não estás subscrito e não te vamos cobrar, mas a tua conta ainda está ativa, por agora."
status_unsubscribed: "Ganha acesso a novos níveis, heróis, itens e gemas de bónus com uma subscrição do CodeCombat!"
# not_yet_verified: "Not yet verified."
# resend_email: "Resend email"
# email_sent: "Email sent! Check your inbox."
# verifying_email: "Verifying your email address..."
# successfully_verified: "You've successfully verified your email address!"
# back_to_student_page: "Go back to student things"
# back_to_teacher_page: "Go to My Classes"
# back_to_game: "Go play some more levels!"
# verify_error: "Something went wrong when verifying your email :("
not_yet_verified: "Ainda não foi verificado."
resend_email: "Reenviar e-mail"
email_sent: "E-mail enviado! Verifica a tua caixa de entrada."
verifying_email: "A verificar o teu endereço de e-mail..."
successfully_verified: "Verificaste o teu endereço de e-mail com sucesso!"
back_to_student_page: "Voltar às coisas de estudante"
back_to_teacher_page: "Ir para As Minhas Turmas"
back_to_game: "Ir jogar mais alguns níveis!"
verify_error: "Algo correu mal aquando da verificação do teu e-mail :("
account_invoices:
amount: "Quantidade em dólares americanos"
@ -1844,7 +1844,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
coppa_deny:
text1: "Não podes esperar para aprender programação?"
text2: "Pede aos teus educadores para criarem uma conta para ti!" # {change}
text2: "Os teus educadores vão precisar de criar uma conta para tu usares! Envia um e-mail para team@codecombat.com se tiveres alguma pergunta."
close: "Fechar Janela"
loading_error:

View file

@ -2,6 +2,8 @@
//- Fixed save button
#site-content-area
width: auto
max-width: 1024px
padding-bottom: 44px
#save-button-container
@ -20,9 +22,7 @@
opacity: 1.0
#account-settings-view
.row
padding-top: 20px
padding-top: 20px
//- Panels
.panel-heading

View file

@ -10,15 +10,17 @@ else
.panel-body
.form
- var name = me.get('name') || '';
- var firstName = me.get('firstName') || '';
- var lastName = me.get('lastName') || '';
- var email = me.get('email') || '';
- var admin = me.get('permissions', true).indexOf('admin') != -1;
- var godmode = me.get('permissions', true).indexOf('godmode') != -1;
.form-group
label.control-label(for="name", data-i18n="general.name") Name
label.control-label(for="name", data-i18n="general.username")
input#name-input.form-control(name="name", type="text", value="#{name}")
.form-group
label.control-label(for="email")
span(data-i18n="general.email") Email
span(data-i18n="general.email")
unless me.get('emailVerified')
span.spl (
span.spr(data-i18n="account.not_yet_verified")
@ -27,12 +29,23 @@ else
span.sent-text.hide(data-i18n="account.email_sent")
span )
input#email.form-control(name="email", type="text", value="#{email}")
.row
.form-group.col-sm-6
label.control-label(for="firstName", data-i18n="general.first_name")
input#first-name-input.form-control(name="firstName", type="text", value="#{firstName}")
.form-group.col-sm-6
if me.isStudent()
label.control-label(for="lastName", data-i18n="general.last_initial")
input#last-name-input.form-control(name="lastName", type="text", value="#{lastName}", maxlength="1")
else
label.control-label(for="lastName", data-i18n="general.last_name")
input#last-name-input.form-control(name="lastName", type="text", value="#{lastName}")
if !isProduction
.form-group.checkbox
label(for="admin", data-i18n="account_settings.admin") Admin
label(for="admin", data-i18n="account_settings.admin")
input#admin(name="admin", type="checkbox", checked=admin)
.form-group.checkbox
label(for="godmode", data-i18n="account_settings.god_mode") God Mode
label(for="godmode", data-i18n="account_settings.god_mode")
input#godmode(name="godmode", type="checkbox", checked=godmode)
if me.hasSubscription()
.form-group

View file

@ -73,6 +73,9 @@ block content
if me.isStudent()
button.remain-student-btn.btn.btn-navy(data-i18n="courses.update_account_remain_student")
else
label
span(data-i18n="courses.need_a_class_code")
input(name='classCode')
button.update-student-btn.btn.btn-forest(data-i18n="courses.update_account_update_student")
br
br

View file

@ -266,6 +266,8 @@ module.exports = class AccountSettingsView extends CocoView
grabOtherData: ->
@$el.find('#name-input').val @suggestedName if @suggestedName
me.set 'name', @$el.find('#name-input').val()
me.set 'firstName', @$el.find('#first-name-input').val()
me.set 'lastName', @$el.find('#last-name-input').val()
me.set 'email', @$el.find('#email').val()
for emailName, enabled of @getSubscriptions()
me.setEmailSubscription emailName, enabled

View file

@ -2,6 +2,7 @@ errors = require 'core/errors'
RootView = require 'views/core/RootView'
template = require 'templates/courses/courses-update-account-view'
AuthModal = require 'views/core/AuthModal'
JoinClassModal = require 'views/courses/JoinClassModal'
{logoutUser, me} = require('core/auth')
module.exports = class CoursesUpdateAccountView extends RootView
@ -41,8 +42,11 @@ module.exports = class CoursesUpdateAccountView extends RootView
@becomeStudent(e.target, 'Remain student')
onClickUpdateStudentButton: (e) ->
return unless window.confirm($.i18n.t('courses.update_account_confirm_update_student') + '\n\n' + $.i18n.t('courses.update_account_confirm_update_student2'))
@becomeStudent(e.target, 'Update student')
joinClassModal = new JoinClassModal { classCode: @$('input[name="classCode"]').val() }
@openModalView joinClassModal
@listenTo joinClassModal, 'join:success', => @becomeStudent(e.target, 'Update student')
# return unless window.confirm($.i18n.t('courses.update_account_confirm_update_student') + '\n\n' + $.i18n.t('courses.update_account_confirm_update_student2'))
# @becomeStudent(e.target, 'Update student')
becomeStudent: (targetElem, trackEventMsg) ->
$(targetElem).prop('disabled', true)

View file

@ -10,7 +10,7 @@ module.exports = class JoinClassModal extends ModalView
events:
'click .join-class-btn': 'onClickJoinClassButton'
initialize: ({ @classCode }) ->
initialize: ({ @classCode } = {}) ->
@classroom = new Classroom()
@teacher = new User()
jqxhr = @supermodel.trackRequest @classroom.fetchByCode(@classCode)

View file

@ -22,7 +22,7 @@ co = require 'co'
module.exports =
fetchByCode: wrap (req, res, next) ->
code = req.query.code
return next() unless code
return next() unless req.query.hasOwnProperty('code')
classroom = yield Classroom.findOne({ code: code.toLowerCase().replace(RegExp(' ', 'g') , '') }).select('name ownerID aceConfig')
if not classroom
log.debug("classrooms.fetchByCode: Couldn't find Classroom with code: #{code}")

View file

@ -370,6 +370,11 @@ UserSchema.pre('save', (next) ->
@set('name', undefined)
@set('nameLower', undefined)
if _.isEmpty(@get('firstName'))
@set('firstName', undefined)
if _.isEmpty(@get('lastName'))
@set('lastName', undefined)
unless email or name or @get('anonymous') or @get('deleted')
return next(new errors.UnprocessableEntity('User needs a username or email address'))

View file

@ -24,8 +24,9 @@ describe '/courses/update-account', ->
it 'shows update to teacher button', ->
expect(@view.$el.find('.update-teacher-btn').length).toEqual(1)
it 'shows update to student button', ->
it 'shows update to student button and classCode input', ->
expect(@view.$el.find('.update-student-btn').length).toEqual(1)
expect(@view.$el.find('input[name="classCode"]').length).toEqual(1)
describe 'when logged in as student', ->
beforeEach (done) ->
@ -39,6 +40,7 @@ describe '/courses/update-account', ->
it 'shows remain a student button', ->
expect(@view.$el.find('.remain-student-btn').length).toEqual(1)
expect(@view.$el.find('input[name="classCode"]').length).toEqual(0)
it 'shows update to teacher button', ->
expect(@view.$el.find('.update-teacher-btn').length).toEqual(1)
@ -57,3 +59,4 @@ describe '/courses/update-account', ->
it 'shows update to student button', ->
expect(@view.$el.find('.update-student-btn').length).toEqual(1)
expect(@view.$el.find('input[name="classCode"]').length).toEqual(1)

View file

@ -24,7 +24,12 @@ describe 'TeacherClassView', ->
beforeEach (done) ->
me = factories.makeUser({})
@courses = new Courses([factories.makeCourse({name: 'First Course'}), factories.makeCourse({name: 'Second Course'})])
@courses = new Courses([
factories.makeCourse({name: 'First Course'}),
factories.makeCourse({name: 'Second Course'}),
factories.makeCourse({name: 'Beta Course', releasePhase: 'beta'}),
])
@releasedCourses = new Courses(@courses.where({ releasePhase: 'released' }))
available = factories.makePrepaid()
expired = factories.makePrepaid({endDate: moment().subtract(1, 'day').toISOString()})
@students = new Users([
@ -35,11 +40,11 @@ describe 'TeacherClassView', ->
factories.makeUser({name: 'Ned'}, {prepaid: expired})
factories.makeUser({name: 'Ebner'}, {prepaid: expired})
])
@levels = new Levels(_.times(2, -> factories.makeLevel()))
@classroom = factories.makeClassroom({}, { @courses, members: @students, levels: [@levels, new Levels()] })
@levels = new Levels(_.times(2, -> factories.makeLevel({ concepts: ['basic_syntax', 'arguments', 'functions'] })))
@classroom = factories.makeClassroom({}, { courses: @releasedCourses, members: @students, levels: [@levels, new Levels()] })
@courseInstances = new CourseInstances([
factories.makeCourseInstance({}, { course: @courses.first(), @classroom, members: @students })
factories.makeCourseInstance({}, { course: @courses.last(), @classroom, members: @students })
factories.makeCourseInstance({}, { course: @releasedCourses.first(), @classroom, members: @students })
factories.makeCourseInstance({}, { course: @releasedCourses.last(), @classroom, members: @students })
])
sessions = []
@ -47,11 +52,11 @@ describe 'TeacherClassView', ->
@unfinishedStudent = @students.last()
for level in @levels.models
sessions.push(factories.makeLevelSession(
{state: {complete: true}},
{state: {complete: true}, playtime: 60},
{level, creator: @finishedStudent})
)
sessions.push(factories.makeLevelSession(
{state: {complete: true}},
{state: {complete: true}, playtime: 60},
{level: @levels.first(), creator: @unfinishedStudent})
)
@levelSessions = new LevelSessions(sessions)
@ -118,3 +123,25 @@ describe 'TeacherClassView', ->
users = @view.enrollStudents.calls.argsFor(0)[0]
expect(users.size()).toBe(1)
expect(users.first().id).toBe(@view.students.first().id)
describe 'Export Student Progress (CSV) button', ->
it 'downloads a CSV file', ->
spyOn(window, 'open').and.callFake (encodedCSV) =>
progressData = decodeURI(encodedCSV)
CSVHeader = 'data:text\/csv;charset=utf-8,'
expect(progressData).toMatch new RegExp('^' + CSVHeader)
lines = progressData.slice(CSVHeader.length).split('\n')
expect(lines.length).toBe(@students.length + 1)
for line in lines
simplerLine = line.replace(/"[^"]+"/g, '""')
# Username,Email,Total Playtime, [CS1-? Playtime], Concepts
expect(simplerLine.match(/[^,]+/g).length).toBe(3 + @releasedCourses.length + 1)
if simplerLine.match new RegExp(@finishedStudent.get('email'))
expect(simplerLine).toMatch /2 minutes,2 minutes,0/
else if simplerLine.match new RegExp(@unfinishedStudent.get('email'))
expect(simplerLine).toMatch /a minute,a minute,0/
else if simplerLine.match /@/
expect(simplerLine).toMatch /0,0,0/
return true
@view.$('.export-student-progress-btn').click()
expect(window.open).toHaveBeenCalled()