Add cypress test for mentor invitation
This commit is contained in:
parent
f150751219
commit
3f02fd254a
|
|
@ -104,12 +104,13 @@ const noLearningMentors = computed(() =>
|
|||
</div>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-2">
|
||||
<main>
|
||||
<main data-cy="my-mentors-list">
|
||||
<div>
|
||||
<div
|
||||
v-for="invitation in invitations"
|
||||
:key="invitation.id"
|
||||
class="flex flex-col justify-between gap-4 border-b py-2 last:border-b-0 md:flex-row md:gap-16"
|
||||
:data-cy="`mentor-${invitation.email}`"
|
||||
>
|
||||
<div class="flex flex-col md:flex-grow md:flex-row">
|
||||
<div class="flex items-center space-x-2">
|
||||
|
|
@ -178,6 +179,7 @@ const noLearningMentors = computed(() =>
|
|||
<button
|
||||
:disabled="!validEmail"
|
||||
class="btn-primary mt-8"
|
||||
data-cy="invite-mentor-button"
|
||||
@click="inviteMentor()"
|
||||
>
|
||||
{{ $t("a.Einladung abschicken") }}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,36 @@
|
|||
<script setup lang="ts">
|
||||
import { useCSRFFetch } from "@/fetchHelpers";
|
||||
import { itPost } from "@/fetchHelpers";
|
||||
import { getLearningMentorUrl } from "@/utils/utils";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseId: string;
|
||||
invitationId: string;
|
||||
}>();
|
||||
|
||||
const { data, error } = useCSRFFetch(
|
||||
`/api/mentor/${props.courseId}/invitations/accept`,
|
||||
{
|
||||
onFetchError(ctx) {
|
||||
ctx.error = ctx.data;
|
||||
return ctx;
|
||||
},
|
||||
}
|
||||
)
|
||||
.post({
|
||||
const loaded = ref<boolean>(false);
|
||||
const responseData = ref<any>(null);
|
||||
const hasError = ref<boolean>(false);
|
||||
const errorMessage = ref<string>("");
|
||||
|
||||
onMounted(async () => {
|
||||
const url = `/api/mentor/${props.courseId}/invitations/accept`;
|
||||
itPost(url, {
|
||||
invitation_id: props.invitationId,
|
||||
})
|
||||
.json();
|
||||
.then((data) => {
|
||||
responseData.value = data;
|
||||
})
|
||||
.catch((error) => {
|
||||
hasError.value = true;
|
||||
if (error.toString().includes("404")) {
|
||||
errorMessage.value = "Einladung bereits akzeptiert";
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loaded.value = true;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -28,9 +39,9 @@ const { data, error } = useCSRFFetch(
|
|||
<header class="mb-8 mt-12">
|
||||
<h1 class="mb-8">{{ $t("a.Einladung") }}</h1>
|
||||
</header>
|
||||
<main>
|
||||
<main v-if="loaded">
|
||||
<div class="bg-white p-6">
|
||||
<template v-if="error">
|
||||
<template v-if="hasError">
|
||||
{{
|
||||
$t(
|
||||
"a.Die Einladung konnte nicht akzeptiert werden. Bitte melde dich beim Support."
|
||||
|
|
@ -50,8 +61,8 @@ const { data, error } = useCSRFFetch(
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="error.message" class="my-4">
|
||||
{{ $t("a.Fehlermeldung") }}: {{ error.message }}
|
||||
<div v-if="errorMessage" class="my-4">
|
||||
{{ $t("a.Fehlermeldung") }}: {{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
|
|
@ -61,11 +72,16 @@ const { data, error } = useCSRFFetch(
|
|||
"
|
||||
>
|
||||
<template #name>
|
||||
<b>{{ data.user.first_name }} {{ data.user.last_name }}</b>
|
||||
<b>
|
||||
{{ responseData.user.first_name }} {{ responseData.user.last_name }}
|
||||
</b>
|
||||
</template>
|
||||
</i18next>
|
||||
<div class="mt-4">
|
||||
<a class="underline" :href="getLearningMentorUrl(data.course_slug)">
|
||||
<a
|
||||
class="underline"
|
||||
:href="getLearningMentorUrl(responseData.course_slug)"
|
||||
>
|
||||
{{ $t("a.Übersicht anschauen") }}
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const signUpURL = computed(() => getSignUpURL(constructParams()));
|
|||
</a>
|
||||
|
||||
<p class="mb-4 mt-12">{{ $t("a.Hast du schon ein Konto?") }}</p>
|
||||
<a :href="loginURL" class="btn-secondary">
|
||||
<a :href="loginURL" class="btn-secondary" data-cy="login-button">
|
||||
{{ $t("a.Anmelden") }}
|
||||
</a>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ export default defineConfig(({ mode }) => {
|
|||
],
|
||||
define: {},
|
||||
server: {
|
||||
host: true,
|
||||
port: 5173,
|
||||
hmr: { port: 5173 },
|
||||
strictPort: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
const { defineConfig } = require("cypress");
|
||||
const { cloudPlugin } = require("cypress-cloud/plugin");
|
||||
import { defineConfig } from "cypress";
|
||||
import { cloudPlugin } from "cypress-cloud/plugin";
|
||||
import tasks from "./cypress/plugins/index.mjs";
|
||||
|
||||
module.exports = defineConfig({
|
||||
export default defineConfig({
|
||||
projectId: "RVEZS1",
|
||||
chromeWebSecurity: false,
|
||||
watchForFileChanges: false,
|
||||
video: true,
|
||||
viewportWidth: 1280,
|
||||
|
|
@ -19,6 +21,7 @@ module.exports = defineConfig({
|
|||
e2e: {
|
||||
// experimentalSessionAndOrigin: true,
|
||||
setupNodeEvents(on, config) {
|
||||
tasks(on, config);
|
||||
return cloudPlugin(on, config);
|
||||
},
|
||||
baseUrl: "http://localhost:8001",
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import { login } from "../helpers";
|
||||
import { TEST_STUDENT1_VV_USER_ID } from "../../consts";
|
||||
|
||||
describe("mentorInvitation.cy.js", () => {
|
||||
beforeEach(() => {
|
||||
cy.manageCommand("cypress_reset");
|
||||
});
|
||||
|
||||
it("Teilnehmer macht lädt Lernbegleitung ein; Lernbegleitung akzeptiert Einladung", () => {
|
||||
login("student-vv@eiger-versicherungen.ch", "test");
|
||||
cy.visit("/course/versicherungsvermittler-in/learn");
|
||||
cy.get("[data-cy=navigation-learning-mentor-link]").click();
|
||||
cy.get('[data-cy="lm-invite-mentor-button"]').click();
|
||||
cy.get("#mentor-email").type("empty@example.com");
|
||||
cy.get('[data-cy="invite-mentor-button"]').click();
|
||||
|
||||
cy.get('[data-cy="mentor-empty@example.com"]').should(
|
||||
"contain",
|
||||
"Die Einladung wurde noch nicht angenommen.",
|
||||
);
|
||||
|
||||
cy.task(
|
||||
"runSql",
|
||||
"select target_url from learning_mentor_mentorinvitation where email = 'empty@example.com'",
|
||||
).then((res) => {
|
||||
const invitationUrl = res.rows[0].target_url;
|
||||
console.log(invitationUrl);
|
||||
|
||||
cy.visit("/");
|
||||
cy.get('[data-cy="header-profile"]').click();
|
||||
cy.get('[data-cy="logout-button"]').click();
|
||||
cy.wait(1000);
|
||||
|
||||
// try to accept invitation
|
||||
cy.visit(invitationUrl);
|
||||
cy.get('[data-cy="login-button"]').click();
|
||||
cy.get("#username").type("empty@example.com");
|
||||
cy.get("#password").type("test");
|
||||
cy.get('[data-cy="login-button"]').click();
|
||||
|
||||
cy.get(".bg-white").should(
|
||||
"contain",
|
||||
"Du hast die Einladung von Viktor Vollgas erfolgreich akzeptiert.",
|
||||
);
|
||||
cy.contains("Übersicht anschauen").click();
|
||||
|
||||
cy.get('[data-cy="lm-my-mentees"]').should(
|
||||
"contain",
|
||||
"Personen, die du begleitest",
|
||||
);
|
||||
cy.get('[data-cy="lm-my-mentees"]').should(
|
||||
"contain",
|
||||
"student-vv@eiger-versicherungen.ch",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { runSql } from "./tasks.mjs";
|
||||
|
||||
export default (on, config) => {
|
||||
on("task", {
|
||||
runSql,
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import pg from "pg";
|
||||
|
||||
const cypressDatabaseUrl = process.env?.CYPRESS_DATABASE_URL || "postgres://postgres@localhost:5432/vbv_lernwelt_cypress";
|
||||
|
||||
if(!cypressDatabaseUrl) {
|
||||
throw new Error(
|
||||
"CYPRESS_DATABASE_URL must be set"
|
||||
);
|
||||
}
|
||||
|
||||
export async function runSql(sqlString) {
|
||||
// I could not make postgres.js make work, so I use pg directly
|
||||
const client = new pg.Client(cypressDatabaseUrl);
|
||||
await client.connect();
|
||||
const res = await client.query(sqlString);
|
||||
await client.end();
|
||||
return res;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -9,7 +9,8 @@
|
|||
"prettier": "npm run prettier --prefix client"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^12.15.0",
|
||||
"cypress-cloud": "^1.7.4"
|
||||
"cypress": "^12.17.4",
|
||||
"cypress-cloud": "^1.10.2",
|
||||
"pg": "^8.12.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ from vbv_lernwelt.course.services import mark_course_completion
|
|||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
|
||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
|
||||
from vbv_lernwelt.learnpath.models import (
|
||||
LearningContentAttendanceCourse,
|
||||
LearningContentFeedbackUK,
|
||||
|
|
@ -145,6 +145,7 @@ def command(
|
|||
CourseCompletionFeedback.objects.all().delete()
|
||||
|
||||
LearningMentor.objects.all().delete()
|
||||
MentorInvitation.objects.all().delete()
|
||||
User.objects.all().update(organisation=Organisation.objects.first())
|
||||
User.objects.all().update(language="de")
|
||||
User.objects.all().update(additional_json_data={})
|
||||
|
|
|
|||
|
|
@ -34,5 +34,5 @@ class LearningMentorAdmin(admin.ModelAdmin):
|
|||
@admin.register(MentorInvitation)
|
||||
class MentorInvitationAdmin(admin.ModelAdmin):
|
||||
list_display = ["id", "email", "participant", "created"]
|
||||
readonly_fields = ["id", "created"]
|
||||
readonly_fields = ["id", "created", "email", "participant"]
|
||||
search_fields = ["email"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.20 on 2024-07-18 08:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("learning_mentor", "0006_auto_20240319_1058"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="mentorinvitation",
|
||||
name="target_url",
|
||||
field=models.CharField(blank=True, default="", max_length=255),
|
||||
),
|
||||
]
|
||||
|
|
@ -34,6 +34,7 @@ class MentorInvitation(TimeStampedModel):
|
|||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
email = models.EmailField()
|
||||
participant = models.ForeignKey(CourseSessionUser, on_delete=models.CASCADE)
|
||||
target_url = models.CharField(max_length=255, blank=True, default="")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.email} ({self.participant})"
|
||||
|
|
|
|||
|
|
@ -143,6 +143,8 @@ def create_invitation(request, course_session_id: int):
|
|||
|
||||
invitation = serializer.save()
|
||||
target_url = f"/lernbegleitung/{course_session_id}/invitation/{invitation.id}"
|
||||
invitation.target_url = target_url
|
||||
invitation.save()
|
||||
|
||||
if course_session.course.configuration.is_uk:
|
||||
template = EmailTemplate.PRAXISBILDNER_INVITATION
|
||||
|
|
|
|||
Loading…
Reference in New Issue