bqb-msal-react-tester
Version:
A npm package to simplify testing react application using msal-react
490 lines (471 loc) • 22.6 kB
JavaScript
function ___$insertStyle(css) {
if (!css || typeof window === 'undefined') {
return;
}
const style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.innerHTML = css;
document.head.appendChild(style);
return css;
}
Object.defineProperty(exports, '__esModule', { value: true });
var msalBrowser = require('@azure/msal-browser');
var react = require('@testing-library/react');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
var TEST_ACCESS_TOKEN = 'eyJhbGciOiJIUzI1NiJ9.eyJzY3AiOiJVc2VyLlJlYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9jb250b3NvIiwiYXBwX2Rpc3BsYXluYW1lIjoiYXBwbGljYXRpb24tbmFtZSIsInRpZCI6InRlbmFudC1pZCIsInRlbmFudF9yZWdpb25fc2NvcGUiOiJYWCIsImF1ZCI6IjAwMDAwMDAzLTAwMDAtMDAwMC1jMDAwLTAwMDAwMDAwMDAwMCIsInVuaXF1ZV9uYW1lIjoiam9obi5kb2VAY29udG9zby5jb20iLCJuYmYiOiIxNjU2NTk5NjI2IiwiYXBwaWQiOiJhcHAtaWQiLCJuYW1lIjoiSm9obiBEb2UiLCJleHAiOiIxNjU2NjA0NzY3IiwiaWF0IjoiMTY1NjU5OTYyNiIsImVtYWlsIjoiam9obi5kb2VAY29udG9zby5jb20ifQ.ftOvEUhqEFKWxIcxcYsgstgldO-31hIVwEhQ0hwwqWg';
//
// Token parsed is equal to :
//
/**
{
"alg": "HS256"
}.{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/contoso",
"nbf": "1656599626",
"iat": "1656599626",
"exp": "1656604767",
"app_displayname": "application-name",
"tid": "tenant-id",
"tenant_region_scope": "XX",
"unique_name": "john.doe@contoso.com",
"appid": "app-id",
"name": "John Doe",
"email": "john.doe@contoso.com",
"scp": "User.Read"
}.[Signature]
*/
var defaultTestAccountInfo = {
homeAccountId: "home-account-id",
localAccountId: "local-account-id",
environment: 'login.windows.net',
tenantId: "tenant-id",
username: 'john.doe@contoso.com',
name: 'John Doe',
};
var defaultTestAuthenticationResult = {
authority: 'https://login.microsoftonline.com',
uniqueId: 'unique-id',
tenantId: 'tenant-id',
scopes: ['openid', 'profile'],
idToken: 'test-id-token',
idTokenClaims: {},
accessToken: TEST_ACCESS_TOKEN,
fromCache: false,
correlationId: 'test-correlation-id',
expiresOn: new Date(Date.now() + 3600000),
account: defaultTestAccountInfo,
tokenType: 'Bearer',
};
var defaultTestAuthError = {
errorCode: 'test-error-code',
errorMessage: 'test-error-message',
subError: '',
correlationId: '',
setCorrelationId: function (correlationId) {
this.correlationId = correlationId;
},
name: 'test-error',
message: 'test-message',
};
/**
* msal-react tester. Useful to tests your components requiring to be logged in, using msal-react
* @example
*
let msalTester: MsalReactTester;
beforeEach(() => {
// new instance of msal tester for each test
msalTester = new MsalReactTester();
// spy all required msal things
msalTester.spyMsal();
});
afterEach(() => {
msalTester.resetSpyMsal();
});
test('Home page render correctly when user is not logged', async () => {
msalTester.isNotLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
expect(screen.getByText(/Please sign-in/)).toBeInTheDocument();
});
*/
var MsalReactTester = /** @class */ (function () {
/**
* Create a new mock IPublicClientApplication instance
* @param testAccountInfo test account you want to use. A default is created if null
* @param testAuthenticationResult test authentication result you want to use . A default is created is null
*/
function MsalReactTester(interationType, testAccountInfo, testAuthenticationResult, testAuthError) {
if (interationType === void 0) { interationType = 'Redirect'; }
if (testAccountInfo === void 0) { testAccountInfo = defaultTestAccountInfo; }
if (testAuthenticationResult === void 0) { testAuthenticationResult = defaultTestAuthenticationResult; }
if (testAuthError === void 0) { testAuthError = defaultTestAuthError; }
this.interationType = interationType;
this._eventCallbacks = [];
this.accounts = [];
this.activeAccount = null;
this._testAccountInfo = testAccountInfo;
this._testAuthenticationResult = testAuthenticationResult;
this.error = testAuthError;
this.client = MsalReactTester.GetNewClient(testAccountInfo, testAuthenticationResult);
}
/**
* Initialize the IPublicClientApplication with an active account.
*/
MsalReactTester.prototype.isLogged = function () {
this.accounts = [this._testAccountInfo];
this.activeAccount = this._testAccountInfo;
};
/**
* Initialize the IPublicClientApplication with no active account
*/
MsalReactTester.prototype.isNotLogged = function () {
this.accounts = [];
this.activeAccount = null;
};
/**
* Reset all spy / mocks. Should be used in `afterEach` call:
*
* @example
* afterEach(() => {
* msalTester.resetSpyMsal();
* });
*/
MsalReactTester.prototype.resetSpyMsal = function () {
jest.resetAllMocks();
this.accounts = [];
this.activeAccount = null;
this._eventCallbacks = [];
};
/**
* Wait for login process to be done
*/
MsalReactTester.prototype.waitForLogin = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, react.waitFor(function () { return expect(_this._handleRedirectSpy).toHaveBeenCalledTimes(1); })];
case 1:
_a.sent();
if (!(this.interationType === 'Redirect')) return [3 /*break*/, 3];
return [4 /*yield*/, react.waitFor(function () { return expect(_this._loginRedirectSpy).toHaveBeenCalledTimes(1); })];
case 2:
_a.sent();
return [3 /*break*/, 5];
case 3: return [4 /*yield*/, react.waitFor(function () { return expect(_this._loginPopupSpy).toHaveBeenCalledTimes(1); })];
case 4:
_a.sent();
_a.label = 5;
case 5: return [2 /*return*/];
}
});
});
};
/**
* Wait for redirect handled by MSAL to be done
*/
MsalReactTester.prototype.waitForRedirect = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, react.waitFor(function () { return expect(_this._handleRedirectSpy).toHaveBeenCalledTimes(1); })];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
/**
* Wait for logout process to be done
*/
MsalReactTester.prototype.waitForLogout = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, react.waitFor(function () { return expect(_this._handleRedirectSpy).toHaveBeenCalledTimes(1); })];
case 1:
_a.sent();
if (!(this.interationType === 'Redirect')) return [3 /*break*/, 3];
return [4 /*yield*/, react.waitFor(function () { return expect(_this._logoutRedirectSpy).toHaveBeenCalledTimes(1); })];
case 2:
_a.sent();
return [3 /*break*/, 5];
case 3: return [4 /*yield*/, react.waitFor(function () { return expect(_this._logoutPopupSpy).toHaveBeenCalledTimes(1); })];
case 4:
_a.sent();
_a.label = 5;
case 5: return [2 /*return*/];
}
});
});
};
/**
* Spy and Mocks required MSAL things. Should be used in `beforeEach` call:
*
* @example
* let msalTester: MsalReactTester;
beforeEach(() => {
// new instance of msal tester for each test
msalTester = new MsalReactTester();
// spy all required msal things
msalTester.spyMsal();
});
* });
*/
MsalReactTester.prototype.spyMsal = function () {
var _this = this;
var eventId = 0;
jest.spyOn(this.client, 'addEventCallback').mockImplementation(function (callbackFn) {
_this._eventCallbacks.push(callbackFn);
eventId += 1;
return eventId.toString();
});
// send a message to say "hey we made redirect start then end"
this._handleRedirectSpy = jest.spyOn(this.client, 'handleRedirectPromise').mockImplementation(function () {
var eventStart = {
eventType: msalBrowser.EventType.HANDLE_REDIRECT_START,
interactionType: msalBrowser.InteractionType.Redirect,
payload: null,
error: null,
timestamp: 10000,
};
_this._eventCallbacks.forEach(function (callback) {
callback(eventStart);
});
var eventEnd = {
eventType: msalBrowser.EventType.HANDLE_REDIRECT_END,
interactionType: msalBrowser.InteractionType.Redirect,
payload: null,
error: null,
timestamp: 10000,
};
_this._eventCallbacks.forEach(function (callback) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
callback(eventEnd);
return [2 /*return*/];
});
}); });
return Promise.resolve(null);
});
this._loginRedirectSpy = jest.spyOn(this.client, 'loginRedirect').mockImplementation(function (request) { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
this.accounts = [this._testAccountInfo];
this.activeAccount = this._testAccountInfo;
eventMessage = {
eventType: msalBrowser.EventType.LOGIN_SUCCESS,
interactionType: msalBrowser.InteractionType.Redirect,
payload: this._testAuthenticationResult,
error: null,
timestamp: 10000,
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve()];
});
}); });
this._loginPopupSpy = jest.spyOn(this.client, "loginPopup").mockImplementation(function (request) { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
this.accounts = [this._testAccountInfo];
this.activeAccount = this._testAccountInfo;
eventMessage = {
eventType: msalBrowser.EventType.LOGIN_SUCCESS,
interactionType: msalBrowser.InteractionType.Popup,
payload: this._testAuthenticationResult,
error: null,
timestamp: 10000
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve(this._testAuthenticationResult)];
});
}); });
this._logoutRedirectSpy = jest.spyOn(this.client, 'logoutRedirect').mockImplementation(function (request) { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
this.accounts = [];
this.activeAccount = null;
eventMessage = {
eventType: msalBrowser.EventType.LOGOUT_SUCCESS,
interactionType: msalBrowser.InteractionType.Redirect,
payload: this._testAuthenticationResult,
error: null,
timestamp: 10000,
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve()];
});
}); });
this._logoutPopupSpy = jest.spyOn(this.client, 'logoutPopup').mockImplementation(function (request) { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
this.accounts = [];
this.activeAccount = null;
eventMessage = {
eventType: msalBrowser.EventType.LOGOUT_SUCCESS,
interactionType: msalBrowser.InteractionType.Popup,
payload: this._testAuthenticationResult,
error: null,
timestamp: 10000,
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve()];
});
}); });
jest.spyOn(this.client, 'getAllAccounts').mockImplementation(function () { return _this.accounts; });
jest.spyOn(this.client, 'getActiveAccount').mockImplementation(function () { return _this.activeAccount; });
jest.spyOn(this.client, 'setActiveAccount').mockImplementation(function (account) { return (_this.activeAccount = account); });
};
MsalReactTester.prototype.generateFailure = function () {
var _this = this;
if (this.interationType === 'Redirect') {
if (this._loginRedirectSpy)
this._loginRedirectSpy.mockClear();
this._loginRedirectSpy = jest.spyOn(this.client, 'loginRedirect').mockImplementation(function (request) { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
eventMessage = {
eventType: msalBrowser.EventType.LOGIN_FAILURE,
interactionType: msalBrowser.InteractionType.Redirect,
payload: null,
error: this.error,
timestamp: 10000,
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve()];
});
}); });
}
else {
if (this._loginPopupSpy)
this._loginPopupSpy.mockClear();
this._loginPopupSpy = jest.spyOn(this.client, "loginPopup").mockImplementation(function () { return __awaiter(_this, void 0, void 0, function () {
var eventMessage;
return __generator(this, function (_a) {
eventMessage = {
eventType: msalBrowser.EventType.LOGIN_FAILURE,
interactionType: msalBrowser.InteractionType.Popup,
payload: null,
error: this.error,
timestamp: 10000
};
this._eventCallbacks.forEach(function (callback) {
callback(eventMessage);
});
return [2 /*return*/, Promise.resolve(null)];
});
}); });
}
};
MsalReactTester.GetNewClient = function (testAccountInfo, testAuthenticationResult) {
var logger = new msalBrowser.Logger({
loggerCallback: function (_level, _message, _containsPii) { },
piiLoggingEnabled: false,
logLevel: msalBrowser.LogLevel.Error,
correlationId: 'mock_test',
});
return {
initialize: function () { return Promise.resolve(); },
acquireTokenPopup: function () { return Promise.resolve(testAuthenticationResult); },
acquireTokenRedirect: function () { return Promise.resolve(); },
acquireTokenSilent: function () { return Promise.resolve(testAuthenticationResult); },
acquireTokenByCode: function () { return Promise.resolve(testAuthenticationResult); },
getAllAccounts: function () { return [testAccountInfo]; },
getAccountByHomeId: function () { return testAccountInfo; },
getAccountByUsername: function () { return testAccountInfo; },
getAccountByLocalId: function () { return testAccountInfo; },
handleRedirectPromise: function () { return Promise.resolve(testAuthenticationResult); },
loginPopup: function () { return Promise.resolve(testAuthenticationResult); },
loginRedirect: function () { return Promise.resolve(); },
logout: function () { return Promise.resolve(); },
logoutRedirect: function () { return Promise.resolve(); },
logoutPopup: function () { return Promise.resolve(); },
ssoSilent: function () { return Promise.resolve(testAuthenticationResult); },
addEventCallback: function () { return null; },
removeEventCallback: function () { return; },
addPerformanceCallback: function () { return ''; },
removePerformanceCallback: function () { return false; },
enableAccountStorageEvents: function () { return; },
disableAccountStorageEvents: function () { return; },
getTokenCache: function () { return null; },
setLogger: function () { return; },
setActiveAccount: function () { return; },
getActiveAccount: function () { return testAccountInfo; },
initializeWrapperLibrary: function () { return; },
setNavigationClient: function () { return; },
getLogger: function () { return logger; },
getConfiguration: function () { return null; },
};
};
return MsalReactTester;
}());
exports.MsalReactTester = MsalReactTester;
//# sourceMappingURL=index.js.map