diff --git a/app/locale/en.coffee b/app/locale/en.coffee index f23728608..8a2e521db 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -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." diff --git a/app/locale/pt-PT.coffee b/app/locale/pt-PT.coffee index fcc6f7e98..5a54eac59 100644 --- a/app/locale/pt-PT.coffee +++ b/app/locale/pt-PT.coffee @@ -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: "" # 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: diff --git a/app/styles/account/account-settings-view.sass b/app/styles/account/account-settings-view.sass index ae8cd31de..1aa995013 100644 --- a/app/styles/account/account-settings-view.sass +++ b/app/styles/account/account-settings-view.sass @@ -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 diff --git a/app/templates/account/account-settings-view.jade b/app/templates/account/account-settings-view.jade index ca424511f..beb395b4d 100644 --- a/app/templates/account/account-settings-view.jade +++ b/app/templates/account/account-settings-view.jade @@ -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 diff --git a/app/templates/courses/courses-update-account-view.jade b/app/templates/courses/courses-update-account-view.jade index 765780987..bfa858eb0 100644 --- a/app/templates/courses/courses-update-account-view.jade +++ b/app/templates/courses/courses-update-account-view.jade @@ -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 diff --git a/app/views/account/AccountSettingsView.coffee b/app/views/account/AccountSettingsView.coffee index 70c945b8c..e8e70ac7b 100644 --- a/app/views/account/AccountSettingsView.coffee +++ b/app/views/account/AccountSettingsView.coffee @@ -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 diff --git a/app/views/courses/CoursesUpdateAccountView.coffee b/app/views/courses/CoursesUpdateAccountView.coffee index 03c7b925b..010d512f0 100644 --- a/app/views/courses/CoursesUpdateAccountView.coffee +++ b/app/views/courses/CoursesUpdateAccountView.coffee @@ -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) diff --git a/app/views/courses/JoinClassModal.coffee b/app/views/courses/JoinClassModal.coffee index 35cb4d58c..b4abc8c24 100644 --- a/app/views/courses/JoinClassModal.coffee +++ b/app/views/courses/JoinClassModal.coffee @@ -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) diff --git a/server/middleware/classrooms.coffee b/server/middleware/classrooms.coffee index d18065f71..b04045fce 100644 --- a/server/middleware/classrooms.coffee +++ b/server/middleware/classrooms.coffee @@ -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}") diff --git a/server/models/User.coffee b/server/models/User.coffee index 0ed8aab85..3d843d00e 100644 --- a/server/models/User.coffee +++ b/server/models/User.coffee @@ -369,6 +369,11 @@ UserSchema.pre('save', (next) -> else @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')) diff --git a/test/app/views/courses/CoursesUpdateAccountView.spec.coffee b/test/app/views/courses/CoursesUpdateAccountView.spec.coffee index 90f6069cc..810278e03 100644 --- a/test/app/views/courses/CoursesUpdateAccountView.spec.coffee +++ b/test/app/views/courses/CoursesUpdateAccountView.spec.coffee @@ -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) diff --git a/test/app/views/teachers/TeacherClassView.spec.coffee b/test/app/views/teachers/TeacherClassView.spec.coffee index 065c128ee..fddf7f301 100644 --- a/test/app/views/teachers/TeacherClassView.spec.coffee +++ b/test/app/views/teachers/TeacherClassView.spec.coffee @@ -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()