Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions cypress/e2e/auth.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ describe("Discord OAuth works", () => {
describe("Welcome Model", () => {
it("Is shown until dismissed", () => {
cy.login("100_DEVER");
tgt.modal.get().contains("Hello, 100Dever#0001");
tgt.modal.get().contains("Hello, 100Dever");

cy.reload();
tgt.modal.get().contains("Hello, 100Dever#0001");
tgt.modal.get().contains("Hello, 100Dever");
});

for (const backdrop of [false, true]) {
it(`Dismissible via ${backdrop ? "backdrop" : "close button"}`, () => {
cy.login("100_DEVER");
tgt.modal.get().contains("Hello, 100Dever#0001");
tgt.modal.get().contains("Hello, 100Dever");
tgt.modal.close(backdrop);

cy.reload();
Expand Down
6 changes: 3 additions & 3 deletions cypress/e2e/create-event-form.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe("Event Creation Form", () => {
tgt.createForm.input.description().type("Test Description");

cy.get('input[name="discordName"]')
.should("have.value", "100Dever#0001")
.should("have.value", "100Dever")
.should("be.disabled");
cy.contains("button", "Back").then(($button) =>
expect($button.css("cursor")).to.equal("not-allowed")
Expand All @@ -95,7 +95,7 @@ describe("Event Creation Form", () => {
"Test Title",
"Test Description",
"Test Location",
"100Dever#0001",
"100Dever",
startDate,
startTime,
endDate,
Expand All @@ -115,7 +115,7 @@ describe("Event Creation Form", () => {
title: "Test Title",
description: "Test Description",
location: "Test Location",
discordName: "100Dever#0001",
discordName: "100Dever",
initialDate: startDate,
finalDate: endDate,
startTime,
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/view-event-modal.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe("View Event Modal", () => {
tgt.modal.get(0).contains("Location: Test Location");
tgt.modal
.get(0)
.contains("Event Author: 100Dever#0001")
.contains("Event Author: 100Dever")
.should(by ? "exist" : "not.exist");
};

Expand Down
54 changes: 47 additions & 7 deletions server/config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ module.exports = function (passport) {
clientSecret: process.env.DISCORD_CLIENT_SECRET,
callbackURL: "/api/auth/discord/callback",
// pulls discord username without email, and returns basic information about all the user's current guilds / servers.
scope: ["identify", "guilds"],
scope: ["identify", "guilds", "guilds.members.read"],
passReqToCallback: true,
},
async function (currentReq, accessToken, refreshToken, profile, cb) {
const displayName =
profile.discriminator.length === 4
? `${profile.username}#${profile.discriminator}`
: profile.username;
// Check if user exists in DB
let user = await User.findOne({ discordId: profile.id }).exec();

//if user is not in 100Devs, return (end the function)
const is100Dever = profile.guilds.some(
(server) => server.id === "735923219315425401"
);
Expand All @@ -39,8 +39,15 @@ module.exports = function (passport) {
currentReq.session.isNot100Dever = true;
return cb(null, false);
}
// Check if user exists in DB
let user = await User.findOne({ discordId: profile.id }).exec();

//check to see if a global name is set for the user. if not, fallback to username
let displayName = profile.global_name ?? profile.username;

// check to see if there is a .nick property in the current user's guild
const serverName = await getServerName(accessToken);

//if there is a serverName, use that instead of username or global_name
if (serverName) displayName = serverName;

try {
// Create user if it doesn't exist
Expand Down Expand Up @@ -73,3 +80,36 @@ module.exports = function (passport) {
)
);
};

/**
* a function that gets a user's server name.
* @param {string} accessToken represents the current access token of the auth instance
*/
async function getServerName(accessToken) {
//if in the parameters, can be reusable + testable
//test the happy path
try {
const devMemberInfo = await fetch(
"https://discord.com/api/users/@me/guilds/735923219315425401/member",
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
const devMemberData = await devMemberInfo.json();
//return a 200, something that is jsonable
//if .nick, that indicates a nickname
if (devMemberData.nick) {
return devMemberData.nick;
}
//if 500, return the response, server name
//if no .nick, return null (prior server name remains)
console.log(`The nickname could not be found.`);
} catch (error) {
console.log(`The member's guild info could not be found: ${error}`);
}
return null;
}

module.exports.getServerName = getServerName;
91 changes: 91 additions & 0 deletions server/test/unit/passport.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use strict";

// Assuming your passport config file is two directories up in /config
const { getServerName } = require("../../config/passport");

describe("getServerName", () => {
beforeEach(() => {
global.fetch = jest.fn();
});

afterEach(() => {
// restore all mocks after each test
jest.restoreAllMocks();
global.fetch.mockClear();
});

/* given nick property, return the nick property */

it("nickname is present", async () => {
// define the mock response for this specific test case
const mockApiResponse = {
nick: "notLegmother",
user: { id: "123", username: "discord-user" },
};

// resolve with an object that has a .json() method,
// which in turn resolves with our mock API response.
jest.spyOn(global, "fetch").mockResolvedValue({
json: jest.fn().mockResolvedValue(mockApiResponse),
});

// call the function, expect it to be the nickname
const result = await getServerName("fake-token");
console.log(result);
expect(result).toBe("notLegmother");
});

//test the cases in which it goes wrong, i.e. no nick property
//capture the user behavior rather than the specific output
it("nickname is not present", async () => {
const mockApiResponse = {
user: { id: "123", username: "discord-user" },
};

// resolve with an object that has a .json() method,
// which in turn resolves with our mock API response.
jest.spyOn(global, "fetch").mockResolvedValue({
json: jest.fn().mockResolvedValue(mockApiResponse),
});

// what is logging? check on that
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation(() => {});

const result = await getServerName("fake-token");

// assertions: return null and the expected log message
expect(result).toBe(null); // The function should return null
expect(consoleLogSpy).toHaveBeenCalledWith(
"The nickname could not be found."
);
});

//server / API error
it("returns no data", async () => {
const mockApiResponse = {
status: 400,
statusText: "Bad Request",
};

// resolve with an object that has a .json() method,
// which in turn resolves with our mock API response.
jest.spyOn(global, "fetch").mockResolvedValue({
json: jest.fn().mockResolvedValue(mockApiResponse),
});

// what is logging? check on that
const consoleLogSpy = jest
.spyOn(console, "log")
.mockImplementation(() => {});

const result = await getServerName("fake-token");

// assertions: return null and the expected log message
expect(result).toBe(null); // The function should return null
expect(consoleLogSpy).toHaveBeenCalledWith(
"The nickname could not be found."
);
});
});