Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exam mode: Add save exercise button to exercises #9569

Merged
merged 22 commits into from
Nov 16, 2024

Conversation

edkaya
Copy link
Contributor

@edkaya edkaya commented Oct 23, 2024

Checklist

General

Client

  • I strictly followed the client coding and design guidelines.
  • Following the theming guidelines, I specified colors only in the theming variable files and checked that the changes look consistent in both the light and the dark theme.
  • I added multiple integration tests (Jest) related to the features (with a high test coverage), while following the test guidelines.
  • I documented the TypeScript code using JSDoc style.
  • I added multiple screenshots/screencasts of my UI changes.
  • I translated all newly inserted strings into English and German.

Motivation and Context

Students can save their exercises only by clicking on a different/same exercise in the sidebar after they are done with the exercise (only in modeling, quiz, and text exercises). However, it may be confusing for the students who are not familiar with Artemis and its exam mode.

Description

  • Added 'Save Exercise' button for modeling, quiz, and text exercises

Steps for Testing

Prerequisites:

  • 1 Student
  • 1 Exam with modeling, text and quiz exercises

(I created an exam with modeling, text and quiz exercises called "Exam Save Exercise Button" under my course on TS5. Test users 1-5 are registered, so you can test the PR with them.)

  1. Log in to Artemis
  2. Participate in the Exam
  3. Navigate to modeling, text, and quiz exercises
  4. Verify that save exercise button is not active
  5. Start working on the exercise and verify that save exercise gets active along with status icon in the sidebar
  6. Click on Save Exercise button
  7. Verify that button is changed to disabled "Exercise Saved" along with the status icon in the sidebar
  8. Navigate to a different exercise and type something and wait
  9. Verify that the button gets disabled and status changed to "Exercise Saved" automatically after 30 seconds

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Test Coverage

Class/File Line Coverage Confirmation (assert/expect)
exercise-save-button.component.ts 100%

Review Progress

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Exam Mode Test

  • Test 1
  • Test 2

Screenshots

Save Exercise
Screenshot 2024-10-24 at 03 06 12

Exercise Saved
Screenshot 2024-10-24 at 03 06 31

Summary by CodeRabbit

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a new ExerciseSaveButton component for saving exercises during participation.
    • Enhanced exam participation interface with save functionality for current exercises and improved rendering logic to prevent errors.
    • Added localization support for save-related messages in both English and German.
    • Improved layout and responsiveness of various exam submission components, including quiz, text, and modeling submissions.
    • Added synchronization status indication for submissions and improved save event handling.
  • Bug Fixes

    • Resolved issues with displaying exercise points and bonus points for better clarity.
  • Documentation

    • Updated test cases to include the new ExerciseSaveButton component.

These updates enhance the user experience by providing better interaction capabilities and clearer feedback during exam participation.

@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Oct 23, 2024
@edkaya edkaya changed the title Exam Mode: Add save exercise button Exam mode: Add save exercise button Oct 23, 2024
@github-actions github-actions bot added the tests label Oct 23, 2024
@edkaya edkaya changed the title Exam mode: Add save exercise button Exam mode: Add save exercise button to exercises Oct 23, 2024
@edkaya edkaya marked this pull request as ready for review October 24, 2024 01:45
@edkaya edkaya requested a review from a team as a code owner October 24, 2024 01:45
Copy link

coderabbitai bot commented Oct 24, 2024

Walkthrough

The changes in this pull request primarily enhance the exam participation interface by introducing the ExerciseSaveButtonComponent and modifying existing components to improve interaction capabilities. The updates include event bindings for saving the current exercise state and new input properties to indicate submission synchronization. Localization files are updated to include new UI strings for saving actions. Various components are adjusted for better layout and responsiveness, aiming to improve user experience during exam participation.

Changes

File Change Summary
src/main/webapp/app/exam/participate/exam-participation.component.html Added (saveCurrentExercise)="triggerSave(false)" to <jhi-quiz-submission-exam>, <jhi-text-editor-exam>, and <jhi-modeling-submission-exam>, and added [isSubmissionSynced] to <jhi-modeling-submission-exam>.
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html Introduced a new button with ID save-exam, linked to the onSave() method, with conditional display based on submission state.
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.scss Added a new CSS class .saved for styling the save button.
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts Created ExerciseSaveButtonComponent with input submission and output save, implementing the onSave method.
src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html Replaced <h3> with a <div> structure for layout improvements and added <jhi-exercise-save-button>.
src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts Added properties isSubmissionSynced and saveCurrentExercise, and method notifyTriggerSave().
src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.html Updated layout to include <jhi-exercise-save-button> within a new <div>.
src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts Added saveCurrentExercise output and notifyTriggerSave() method.
src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html Modified layout to include <jhi-exercise-save-button> within a new <div>.
src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts Introduced saveCurrentExercise output and updated farListAlt to readonly.
src/main/webapp/i18n/de/exam.json Added new localization keys: saveExercise and exerciseSaved.
src/main/webapp/i18n/en/exam.json Added new localization keys: saveExercise and exerciseSaved.
src/test/javascript/spec/component/exam/participate/exercises/modeling-exam-submission.component.spec.ts Added ExerciseSaveButtonComponent to the test declarations.
src/test/javascript/spec/component/exam/participate/exercises/quiz-exam-submission.component.spec.ts Added ExerciseSaveButtonComponent to the test declarations.
src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts Added ExerciseSaveButtonComponent to the test declarations.
src/test/playwright/e2e/exam/ExamDateVerification.spec.ts Updated visibility assertion for "Exercise saved" to require an exact match.
src/main/webapp/app/exam/participate/exercises/file-upload/file-upload-exam-submission.component.html Restructured how exercise points and bonus points are displayed using jhiTranslate for better readability.
src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.html Updated points and bonus points display using jhiTranslate for improved clarity.
src/main/webapp/app/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.html Changed translation key for displaying points to ensure consistency.

Suggested labels

feature, bugfix

Suggested reviewers

  • JohannesStoehr
  • SimonEntholzer
  • pzdr7
  • krusche

Possibly related PRs


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (15)
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html (1)

1-1: Enhance button accessibility and user feedback.

Consider adding aria-label and title attributes to improve accessibility and provide tooltip feedback.

-<button id="save-exam" type="submit" [disabled]="submission()?.isSynced" class="btn btn-primary" (click)="onSave()">
+<button 
+    id="save-exam" 
+    type="submit" 
+    [disabled]="submission()?.isSynced" 
+    class="btn btn-primary" 
+    (click)="onSave()"
+    [attr.aria-label]="submission()?.submitted && submission()?.isSynced ? 'Exercise saved' : 'Save exercise'"
+    [title]="submission()?.submitted && submission()?.isSynced ? 'Exercise is saved' : 'Click to save exercise'">
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts (1)

15-25: Add type safety and documentation to the component.

While the implementation is functional, it could benefit from improved type safety and documentation.

Consider these improvements:

 export class ExerciseSaveButtonComponent {
     protected readonly faFloppyDisk = faFloppyDisk;
     protected readonly facSaveSuccess = facSaveSuccess;
 
-    submission = input<Submission>();
-    save = output<void>();
+    submission = input.required<Submission>();  // Make submission required
+    save = output<void>({ alias: 'exerciseSave' });  // Add alias for clearer API
 
+    /**
+     * Emits a save event when the user clicks the save button.
+     * This triggers the parent component to handle the actual save operation.
+     */
     onSave() {
+        if (!this.submission()) {
+            console.warn('Attempting to save without a submission');
+            return;
+        }
         this.save.emit();
     }
 }
src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html (1)

8-14: Consider extracting nested template logic for better readability.

The nested conditional for bonus points and score badge makes the template harder to read. Consider extracting these into separate template variables or methods.

-    <!-- prettier-ignore -->
-    <span left-header>&nbsp;({{ exercise.maxPoints }} {{ 'artemisApp.examParticipation.points' | artemisTranslate }}@if (exercise.bonusPoints) {
-        <span
-        >, {{ exercise.bonusPoints }} {{ 'artemisApp.examParticipation.bonus' | artemisTranslate }}</span>
-    }) @if (exercise.includedInOverallScore !== IncludedInOverallScore.INCLUDED_COMPLETELY) {
-        <jhi-included-in-score-badge
-        [includedInOverallScore]="exercise.includedInOverallScore" />
-    }</span>
+    <span left-header>
+        &nbsp;({{ exercise.maxPoints }} {{ 'artemisApp.examParticipation.points' | artemisTranslate }}
+        @if (exercise.bonusPoints) {
+            <span>, {{ exercise.bonusPoints }} {{ 'artemisApp.examParticipation.bonus' | artemisTranslate }}</span>
+        })
+        @if (exercise.includedInOverallScore !== IncludedInOverallScore.INCLUDED_COMPLETELY) {
+            <jhi-included-in-score-badge [includedInOverallScore]="exercise.includedInOverallScore" />
+        }
+    </span>
src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html (2)

8-14: Consider improving template readability.

The nested conditional rendering for bonus points and score inclusion could be more readable.

Consider restructuring like this:

-            <!-- prettier-ignore -->
-            <span left-header>&nbsp;({{ exercise.maxPoints }} {{ 'artemisApp.examParticipation.points' | artemisTranslate }}@if (exercise.bonusPoints) {
-                <span
-                >, {{ exercise.bonusPoints }} {{ 'artemisApp.examParticipation.bonus' | artemisTranslate }}</span>
-            }) @if (exercise.includedInOverallScore !== IncludedInOverallScore.INCLUDED_COMPLETELY) {
-                <jhi-included-in-score-badge
-                [includedInOverallScore]="exercise.includedInOverallScore" />
-            }</span>
+            <span left-header>
+                &nbsp;({{ exercise.maxPoints }} {{ 'artemisApp.examParticipation.points' | artemisTranslate }}
+                @if (exercise.bonusPoints) {
+                    <span>, {{ exercise.bonusPoints }} {{ 'artemisApp.examParticipation.bonus' | artemisTranslate }}</span>
+                })
+                @if (exercise.includedInOverallScore !== IncludedInOverallScore.INCLUDED_COMPLETELY) {
+                    <jhi-included-in-score-badge [includedInOverallScore]="exercise.includedInOverallScore" />
+                }
+            </span>

Line range hint 28-36: Add accessibility attributes to the text editor.

The textarea should have an aria-label for better accessibility.

     <textarea
         #textEditor
         id="text-editor"
         class="text-editor-textarea"
+        aria-label="Exercise answer text editor"
         [maxLength]="maxCharacterCount"
         [(ngModel)]="answer"
         [readOnly]="readonly || !studentSubmission"
         [disabled]="readonly || !studentSubmission"
src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts (2)

29-29: Consider adding void type parameter for better type safety.

While the output works, explicitly declaring the type parameter improves code clarity and type safety.

-    saveCurrentExercise = output<void>();
+    saveCurrentExercise = output<void, void>();

127-132: Consider using arrow function for consistency.

While the implementation is correct, using an arrow function would be more consistent with modern TypeScript practices.

-    /**
-     * Trigger save action in exam participation component
-     */
-    notifyTriggerSave() {
-        this.saveCurrentExercise.emit();
-    }
+    /**
+     * Trigger save action in exam participation component
+     */
+    notifyTriggerSave = (): void => {
+        this.saveCurrentExercise.emit();
+    };
src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.html (2)

1-14: LGTM! Clean layout structure with good component organization.

The flexbox layout provides good alignment between the title and save button, matching the PR objectives for improving the exam mode interface.

Consider adding an aria-label to the save button section for better accessibility:

-    <jhi-exercise-save-button [submission]="studentSubmission" (save)="notifyTriggerSave()" />
+    <jhi-exercise-save-button 
+        [submission]="studentSubmission" 
+        (save)="notifyTriggerSave()"
+        aria-label="Save exercise progress"
+    />

Line range hint 82-122: Consider refactoring repeated question rendering patterns.

The question rendering logic follows similar patterns for each type (MC, DD, SA). Consider extracting this into a shared component to improve maintainability.

Example approach:

// question-renderer.component.ts
interface QuestionConfig {
  type: string;
  component: Type<any>;
  idPrefix: string;
}

const QUESTION_COMPONENTS: Record<string, QuestionConfig> = {
  MULTIPLE_CHOICE: { 
    type: 'MULTIPLE_CHOICE',
    component: MultipleChoiceQuestionComponent,
    idPrefix: 'MC'
  },
  // ... similar for other types
};
<!-- question-renderer.component.html -->
<ng-container [ngComponentOutlet]="questionConfig.component"
    [ngComponentOutletInputs]="{
        question: question,
        questionIndex: index,
        // ... other common props
    }">
</ng-container>
src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts (2)

37-41: Consider consistent decorator usage across the component.

The component mixes the new input()/output() syntax with older @Input/@output decorators. Consider updating all properties to use the new syntax for consistency.

Apply this pattern to other properties:

studentSubmission = input<ModelingSubmission>();
exercise = input<ModelingExercise>();

37-39: Enhance documentation for isSubmissionSynced.

While the technical reason for the property is documented, consider adding its business purpose for better maintainability.

/**
 * Tracks the synchronization state of the current submission with the server.
 * Explicitly needed due to OnPush change detection strategy.
 * Used by the save button to indicate whether changes are pending.
 */
isSubmissionSynced = input<boolean>();
src/test/javascript/spec/component/exam/participate/exercises/modeling-exam-submission.component.spec.ts (3)

Line range hint 20-53: Add tests for save button functionality.

The test suite is missing coverage for the new save button functionality described in the PR objectives:

  • Button activation when student starts working
  • Button state changes after saving
  • Auto-save functionality after 30 seconds of inactivity

Consider adding these test cases:

describe('Save button functionality', () => {
    beforeEach(() => {
        resetComponent();
        fixture.detectChanges();
    });

    it('should enable save button when model changes', () => {
        const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
        comp.modelChanged({} as UMLModel);
        fixture.detectChanges();
        
        expect(saveButton.componentInstance.isDisabled).toBeFalse();
    });

    it('should disable save button after saving', () => {
        const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
        comp.onSave();
        fixture.detectChanges();
        
        expect(saveButton.componentInstance.isDisabled).toBeTrue();
        expect(saveButton.componentInstance.label).toBe('Exercise Saved');
    });

    it('should auto-save after 30 seconds of inactivity', fakeAsync(() => {
        const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
        comp.modelChanged({} as UMLModel);
        fixture.detectChanges();
        
        tick(30000);
        
        expect(saveButton.componentInstance.isDisabled).toBeTrue();
        expect(saveButton.componentInstance.label).toBe('Exercise Saved');
    }));
});

Line range hint 171-196: Add save button state update to modelChanged tests.

The modelChanged test should verify that the save button becomes enabled when the model changes.

Update the test:

describe('modelChanged', () => {
    beforeEach(() => {
        resetComponent();
        fixture.detectChanges();
    });
    
    it('should set isSynced to false and enable save button', () => {
        const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
        comp.studentSubmission.isSynced = true;
        
        comp.modelChanged({} as UMLModel);
        fixture.detectChanges();
        
        expect(comp.studentSubmission.isSynced).toBeFalse();
        expect(saveButton.componentInstance.isDisabled).toBeFalse();
    });
});

Line range hint 197-209: Add save button state update to explanationChanged tests.

The explanationChanged test should verify that the save button becomes enabled when the explanation changes.

Update the test:

describe('explanationChanged', () => {
    beforeEach(() => {
        resetComponent();
        fixture.detectChanges();
    });
    
    it('should set explanation text, update isSynced, and enable save button', () => {
        const explanationText = 'New Explanation Text';
        const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
        comp.studentSubmission.isSynced = true;
        
        comp.explanationChanged(explanationText);
        fixture.detectChanges();
        
        expect(comp.studentSubmission.isSynced).toBeFalse();
        expect(comp.explanationText).toEqual(explanationText);
        expect(saveButton.componentInstance.isDisabled).toBeFalse();
    });
});
src/test/javascript/spec/component/exam/participate/exercises/quiz-exam-submission.component.spec.ts (1)

Line range hint 28-196: Add test coverage for the new save button functionality.

The test suite needs additional test cases to cover the new save button functionality requirements from the PR:

  • Button activation when student starts working
  • Button state changes after saving
  • Auto-save behavior after 30 seconds of inactivity

Consider adding these test cases:

it('should enable save button when student makes changes', () => {
    // Setup
    component.quizConfiguration = { quizQuestions: [multipleChoiceQuestion] };
    fixture.detectChanges();
    
    // Act
    component.onSelectionChanged();
    fixture.detectChanges();
    
    // Assert
    const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
    expect(saveButton.componentInstance.disabled).toBeFalse();
});

it('should disable save button with "Exercise Saved" label after saving', () => {
    // Setup
    component.quizConfiguration = { quizQuestions: [multipleChoiceQuestion] };
    fixture.detectChanges();
    
    // Act
    component.onSelectionChanged();
    fixture.detectChanges();
    component.saveExercise();
    fixture.detectChanges();
    
    // Assert
    const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
    expect(saveButton.componentInstance.disabled).toBeTrue();
    expect(saveButton.componentInstance.label).toBe('Exercise Saved');
});

it('should auto-save and update button state after 30 seconds of inactivity', fakeAsync(() => {
    // Setup
    component.quizConfiguration = { quizQuestions: [multipleChoiceQuestion] };
    fixture.detectChanges();
    
    // Act
    component.onSelectionChanged();
    fixture.detectChanges();
    tick(30000);
    
    // Assert
    const saveButton = fixture.debugElement.query(By.directive(ExerciseSaveButtonComponent));
    expect(saveButton.componentInstance.disabled).toBeTrue();
    expect(saveButton.componentInstance.label).toBe('Exercise Saved');
}));
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between dd96df5 and a18c304.

📒 Files selected for processing (17)
  • src/main/webapp/app/exam/participate/exam-participation.component.html (2 hunks)
  • src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts (2 hunks)
  • src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.scss (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts (3 hunks)
  • src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.html (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts (3 hunks)
  • src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html (1 hunks)
  • src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts (4 hunks)
  • src/main/webapp/i18n/de/exam.json (1 hunks)
  • src/main/webapp/i18n/en/exam.json (1 hunks)
  • src/test/javascript/spec/component/exam/participate/exercises/modeling-exam-submission.component.spec.ts (2 hunks)
  • src/test/javascript/spec/component/exam/participate/exercises/quiz-exam-submission.component.spec.ts (2 hunks)
  • src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts (2 hunks)
  • src/test/playwright/e2e/exam/ExamDateVerification.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.scss
🧰 Additional context used
📓 Path-based instructions (14)
src/main/webapp/app/exam/participate/exam-participation.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/i18n/de/exam.json (1)

Pattern src/main/webapp/i18n/de/**/*.json: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".

src/test/javascript/spec/component/exam/participate/exercises/modeling-exam-submission.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/exam/participate/exercises/quiz-exam-submission.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

📓 Learnings (1)
src/test/playwright/e2e/exam/ExamDateVerification.spec.ts (2)
Learnt from: SimonEntholzer
PR: ls1intum/Artemis#8946
File: src/test/playwright/e2e/exam/ExamDateVerification.spec.ts:109-110
Timestamp: 2024-10-08T15:35:48.768Z
Learning: For the pull request `General: Fix and re-enable exam mode e2e tests` (PR #8946), avoid providing "safer alternative" tips as requested by SimonEntholzer.
Learnt from: SimonEntholzer
PR: ls1intum/Artemis#8946
File: src/test/playwright/e2e/exam/ExamDateVerification.spec.ts:109-110
Timestamp: 2024-07-02T20:47:05.487Z
Learning: For the pull request `General: Fix and re-enable exam mode e2e tests` (PR #8946), avoid providing "safer alternative" tips as requested by SimonEntholzer.
🔇 Additional comments (28)
src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html (3)

2-2: LGTM! Icon implementation provides clear visual feedback.

The icon changes appropriately based on the submission state, providing good visual feedback to users.


3-7: LGTM! Responsive design handles mobile views appropriately.

The text is properly hidden on extra small screens while maintaining the icon visible, which is a good responsive design practice.


1-8: Verify the submission state handling.

The button's behavior relies heavily on the submission state. Let's verify the implementation of the submission state management.

✅ Verification successful

Let me gather more information about the component implementation to verify the submission state handling properly.


Let me gather more information about the submission model and its implementation to verify the submission state handling properly.


Based on the code analysis, I can now provide a final response about the submission state handling verification.

The submission state handling is correctly implemented.

The implementation shows a well-structured pattern for handling submission states:

  • The ExerciseSaveButtonComponent receives a submission input signal and properly uses it for button state management
  • The submission model (submission.model.ts) defines the isSynced property used for tracking save state
  • Each exercise type component (text, quiz, modeling, etc.) implements the required submission state handling through:
    • hasUnsavedChanges(): Returns !submission.isSynced
    • updateSubmissionFromView(): Updates submission content from UI
    • updateViewFromSubmission(): Updates UI from submission data

The button's behavior correctly reflects the submission state by:

  • Disabling when submission()?.isSynced is true
  • Showing appropriate icon based on submission()?.submitted && submission()?.isSynced
  • Displaying correct translation key based on the same condition
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the submission state handling implementation
# Expected: Find the submission state management in the component class

# Look for the submission signal/property definition
ast-grep --pattern 'submission = signal<$_>($$$)'

# Look for submission state updates
rg -A 5 'submission\(\)\?' src/main/webapp/app/exam/participate/exercises

Length of output: 1591


Script:

#!/bin/bash
# Look for the component class implementation
fd exercise-save-button.component.ts --exec cat {}

# Look for any submission-related services or models
rg -l "class.*Submission" --type ts

# Look for submission state updates in parent components
fd exercise.component.ts --exec cat {}

Length of output: 48478


Script:

#!/bin/bash
# Look for the submission model definition
cat src/main/webapp/app/entities/submission.model.ts

# Look for the parent component that provides the submission signal
fd exam-submission.component.ts --exec cat {}

Length of output: 49509

src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts (3)

8-14: LGTM! Component decorator follows Angular best practices.

The component is properly configured as standalone with minimal dependencies and follows Angular's style guide.


15-25: LGTM! Component is lightweight and memory-efficient.

The component has no subscriptions requiring cleanup and maintains a simple, focused responsibility.


15-25: Verify exam mode requirements implementation.

The component seems to be missing some required functionality mentioned in the PR objectives:

  • Auto-save after 30s inactivity
  • Button state management (active/disabled)
  • "Exercise Saved" state handling

Let's verify if these are implemented elsewhere:

Would you like help implementing these features in this component?

src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html (3)

1-1: LGTM! Proper usage of new Angular control flow syntax.

The code correctly uses the new @if syntax instead of *ngIf as per guidelines.

Also applies to: 8-14


Line range hint 19-39: LGTM! Well-structured editor implementation.

The modeling editor section is well organized with proper conditional rendering and component bindings.


Line range hint 40-54: Verify XSS protection for problemStatementHtml.

The implementation looks good, but ensure that problemStatementHtml is properly sanitized before being rendered with innerHTML.

✅ Verification successful

Let me try a different approach to verify the XSS protection implementation.


Let me try one more search to find how the problemStatementHtml is processed before being rendered.


Let me search for how the problemStatementHtml is processed before being rendered.


I can now generate the final response based on the findings. The code shows that the problemStatementHtml is properly sanitized through multiple layers:

XSS protection is properly implemented for problemStatementHtml

The implementation is secure because:

  1. The problemStatementHtml is processed through the htmlForMarkdown utility which:

    • Uses DOMPurify.sanitize() for HTML sanitization (as seen in markdown.conversion.util.ts)
    • Has configurable allowed HTML tags and attributes
    • Is thoroughly tested with various security scenarios (as seen in markdown.service.spec.ts)
  2. The component initializes problemStatementHtml safely:

this.problemStatementHtml = htmlForMarkdown(this.exercise?.problemStatement);
  1. The update method also uses the same safe conversion:
updateProblemStatement(newProblemStatementHtml: string): void {
    this.problemStatementHtml = newProblemStatementHtml;
    this.changeDetectorReference.detectChanges();
}

The implementation follows Angular security best practices by using proper HTML sanitization before rendering content with [innerHTML].

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for DomSanitizer usage in the component
ast-grep --pattern 'DomSanitizer.bypassSecurityTrustHtml($_)'

Length of output: 187


Script:

#!/bin/bash
# Search for the component file to analyze sanitization
fd "modeling-exam-submission.component.ts" --type f

# Search for DomSanitizer imports and usage
rg "DomSanitizer|sanitize|sanitization" -A 3

Length of output: 74504


Script:

#!/bin/bash
# Search for the component file to analyze how problemStatementHtml is processed
cat src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts

# Search for updateProblemStatement method
rg "updateProblemStatement" -A 5

Length of output: 337020


Script:

#!/bin/bash
# Search for how problemStatementHtml is initialized and processed
rg "problemStatementHtml" -A 5 src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts

# Search for htmlForMarkdown utility
rg "htmlForMarkdown" -A 5

Length of output: 58969

src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html (2)

1-1: LGTM! Proper usage of new Angular control flow syntax.

The @if directives are correctly used instead of *ngIf as per the coding guidelines.

Also applies to: 8-14


Line range hint 31-31: Verify maxLength implementation.

The text editor has a maxLength binding to maxCharacterCount. Let's verify this implementation to ensure proper character limit enforcement.

✅ Verification successful

Let me gather more information to verify the maxLength implementation completely.


Let me search for the actual template implementation to verify how the maxLength is bound to the editor.


maxLength is properly implemented and enforced

The verification shows that the maxLength implementation is correct and consistent:

  1. The maxCharacterCount constant is defined as 30,000 characters in both frontend (MAX_SUBMISSION_TEXT_LENGTH = 30 * 1000) and backend (MAX_SUBMISSION_TEXT_LENGTH = 30_000).
  2. The text editor component correctly binds this limit using [maxLength]="maxCharacterCount" in the template.
  3. The backend also enforces this limit through the @Size annotation on the text field.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for maxCharacterCount definition and usage
ast-grep --pattern 'maxCharacterCount = $_'
rg -A 2 'maxCharacterCount.*='

Length of output: 973


Script:

#!/bin/bash
# Search for MAX_SUBMISSION_TEXT_LENGTH constant definition
rg "MAX_SUBMISSION_TEXT_LENGTH.*=" -A 2

# Search for maxLength binding in the template
rg "maxLength.*textEditor" -B 2 -A 2

# Search for any text editor related configurations
ast-grep --pattern 'textEditor = $_'

Length of output: 1720


Script:

#!/bin/bash
# Search for the template file content
cat src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.html

# Search for any text editor related directives or bindings
rg -l "TextEditorComponent" 
rg "\[maxLength\]" -B 2 -A 2

Length of output: 14331

src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts (1)

21-21: Verify the ExerciseSaveButtonComponent implementation

Let's verify that the component is properly implemented with the required functionality as per PR objectives.

✅ Verification successful

Let's gather more information about the component implementation to make a proper verification.


Based on the gathered information, I can now provide a final response about the component implementation.

ExerciseSaveButtonComponent implementation is properly structured and functional

The component implementation shows a well-structured Angular component with:

  • Proper input handling for Submission object using the new input syntax
  • Output event emitter for save actions
  • Reactive template with conditional rendering for save/saved states
  • Appropriate button states (disabled when synced)
  • Internationalization support through jhiTranslate
  • Icon handling for both saving and saved states
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the ExerciseSaveButtonComponent implementation

# Test 1: Check if the component file exists and contains required features
echo "Checking ExerciseSaveButtonComponent implementation..."
rg -l --type ts "export class ExerciseSaveButtonComponent"

# Test 2: Verify template contains save button with proper states
echo "Checking template for save button implementation..."
rg -l --type html "Exercise Saved" ./src/main/webapp/app/exam/participate/exercises/exercise-save-button/

# Test 3: Check for test file existence
echo "Checking for component tests..."
rg -l --type ts "describe.*ExerciseSaveButtonComponent" ./src/main/webapp/app/exam/participate/exercises/exercise-save-button/

Length of output: 704


Script:

#!/bin/bash
# Let's examine the actual implementation of the component

# Check the component implementation
echo "Component Implementation:"
rg -A 20 "export class ExerciseSaveButtonComponent" src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts

# Check the template content
echo -e "\nTemplate Content:"
cat src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.html

# Check for any Input/Output decorators and dependencies
echo -e "\nDecorators and Dependencies:"
rg "@(Input|Output|Injectable)" src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts

Length of output: 1361

src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts (2)

1-1: LGTM: Import changes are correct and follow Angular style guide.

The addition of output import and the switch to solid icons are appropriate for the new save functionality.

Also applies to: 9-9


40-40: LGTM: Good use of readonly for immutable icon reference.

Making the icon property readonly prevents accidental modifications and follows Angular best practices.

src/test/javascript/spec/component/exam/participate/exercises/text-exam-submission.component.spec.ts (2)

20-20: LGTM: Import statement correctly added.

The import for ExerciseSaveButtonComponent is properly placed with other component imports.


43-43: LGTM: Component correctly mocked.

The ExerciseSaveButtonComponent is properly mocked using NgMocks.MockComponent as per coding guidelines.

src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.html (1)

Line range hint 16-52: Address technical debt regarding Map vs Array consistency.

The TODO comments suggest inconsistencies in data structure usage. This should be addressed to maintain code quality.

Let's check for similar patterns in the codebase:

Would you like help creating a GitHub issue to track the Map vs Array consistency improvements?

src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts (2)

1-1: LGTM! Import changes align with Angular style guide.

The addition of input and output imports from @angular/core is appropriate for the new functionality.


40-40: Verify integration with ExerciseSaveButtonComponent.

Let's verify that the saveCurrentExercise output is properly connected to the ExerciseSaveButtonComponent in the template.

✅ Verification successful

Let me try different commands to verify this integration.


Let me check the content of the template file to verify the integration.


Let me verify the connection between notifyTriggerSave() and saveCurrentExercise in the component.


Integration with ExerciseSaveButtonComponent is properly implemented

The verification shows that:

  • The template includes <jhi-exercise-save-button> with a (save) event binding to notifyTriggerSave()
  • The notifyTriggerSave() method correctly emits the saveCurrentExercise output event
  • The component properly connects the save button's event to the parent component through the output binding
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the usage of saveCurrentExercise in the template
rg -t html "saveCurrentExercise" "src/main/webapp/app/exam/participate/exercises/modeling"

# Verify ExerciseSaveButtonComponent is properly imported
rg -t typescript "ExerciseSaveButtonComponent" "src/main/webapp/app/exam/participate/exercises/modeling"

Length of output: 226


Script:

#!/bin/bash
# Search for the template file corresponding to the component
fd modeling-exam-submission.component.html src/main/webapp/app/exam/participate/exercises/modeling

# Search for saveCurrentExercise in HTML files (using just the extension)
rg "saveCurrentExercise" --type-add 'html:*.html' -t html src/main/webapp/app/exam/participate/exercises/modeling

# Search for ExerciseSaveButtonComponent in TypeScript files
rg "ExerciseSaveButtonComponent" --type-add 'ts:*.ts' -t ts src/main/webapp/app/exam/participate/exercises/modeling

Length of output: 426


Script:

#!/bin/bash
# Check the content of the template file
cat src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.html

# Also search for any exercise-save-button usage in HTML files
rg "<jhi-exercise-save-button" --type-add 'html:*.html' -t html src/main/webapp/app/exam/participate/exercises/modeling

Length of output: 3321


Script:

#!/bin/bash
# Check the TypeScript component implementation for notifyTriggerSave method
rg "notifyTriggerSave" --type-add 'ts:*.ts' -t ts src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts -A 5

Length of output: 223

src/test/javascript/spec/component/exam/participate/exercises/modeling-exam-submission.component.spec.ts (1)

20-20: LGTM: Component properly imported and mocked.

The ExerciseSaveButtonComponent is correctly imported and declared using MockComponent in the TestBed configuration, following the coding guidelines for mocking components.

Also applies to: 53-53

src/test/javascript/spec/component/exam/participate/exercises/quiz-exam-submission.component.spec.ts (1)

28-28: LGTM! Proper integration of ExerciseSaveButtonComponent in test setup.

The import and declaration of the ExerciseSaveButtonComponent follow the correct patterns and testing guidelines.

Also applies to: 59-59

src/main/webapp/app/exam/participate/exam-participation.component.html (4)

62-62: LGTM: Save functionality added to quiz submission.

The addition of the save event handler is consistent with the PR objectives and matches the implementation pattern used across other exercise types.


72-76: LGTM: Save functionality added to text editor.

The save event handler is properly implemented and follows the same pattern as other exercise types.


Line range hint 1-276: LGTM: Template follows latest Angular control flow syntax.

The template correctly uses the new @if and @for syntax as required by the coding guidelines, replacing the older *ngIf and *ngFor directives.


79-84: ⚠️ Potential issue

Consider adding null safety checks for submissions array access.

While the save functionality is properly implemented, the submissions![0] access could be unsafe if the array is empty. Consider adding a null check or using optional chaining.

Consider updating the sync status binding to include null checks:

-[isSubmissionSynced]="exercise.studentParticipations[0].submissions![0].isSynced"
+[isSubmissionSynced]="exercise.studentParticipations[0]?.submissions?.[0]?.isSynced"
src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts (2)

291-296: LGTM! Well-documented and follows single responsibility principle.

The notifyTriggerSave method is implemented correctly with proper documentation and follows good practices.


56-56: Consider implementing OnDestroy for proper cleanup.

To prevent potential memory leaks, consider implementing the OnDestroy interface and unsubscribing from the EventEmitter when the component is destroyed.

Example implementation:

export class QuizExamSubmissionComponent extends ExamSubmissionComponent implements OnInit, OnDestroy {
    // ... existing code ...

    ngOnDestroy() {
        this.saveCurrentExercise.complete();
        // Call parent's ngOnDestroy if it exists
        if (super.ngOnDestroy) {
            super.ngOnDestroy();
        }
    }
}
src/main/webapp/i18n/en/exam.json (1)

342-343: LGTM! The new translation entries are clear and consistent.

The added translations for the save exercise functionality are well-implemented:

  • Clear and concise button labels
  • Consistent capitalization with other UI elements
  • Matches the requirements from PR objectives

coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 24, 2024
Copy link

@SindiBuklaji SindiBuklaji left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reapprove

Copy link

⚠️ Unable to deploy to test servers ⚠️

Testserver "artemis-test5.artemis.cit.tum.de" is already in use by PR #9774.

@github-actions github-actions bot added the deployment-error Added by deployment workflows if an error occured label Nov 13, 2024
@edkaya edkaya removed the deployment-error Added by deployment workflows if an error occured label Nov 14, 2024
@krusche krusche merged commit f472c21 into develop Nov 16, 2024
42 of 46 checks passed
@krusche krusche deleted the chore/exam-mode/add-save-button branch November 16, 2024 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) component:Exam Mode maintainer-approved The feature maintainer has approved the PR playwright ready to merge small tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.