<commit_before>
const awsContext = require('aws-lambda-mock-context');
const AssertionError = require('assertion-error');
const uuid = require('uuid');

CallbackContext = function (framework, sequenceIndex, locale, requestType) {
	this.framework = framework;
	this.sequenceIndex = sequenceIndex;
	this.locale = locale;
	this.requestType = requestType;
}

/**
 * Formats text via i18n, using the locale that was used for the request.
 */
CallbackContext.prototype.t = function (keys, params) {
	if (!params) params = {}
	if (!params.lng) params.lng = this.locale;
	return this.framework.t(keys, params);
}

/**
 * Throws an assertion error.
 * @param {object} data Data for the error. Can include:
 * `message`: Optionally, a message describing the failure.
 * `expected`: Optionally, the expected value.
 * `actual`: Optionally, the actual value.
 * `operator`: Optionally, the comparison operator that was used.
 * `showDiff`: Optionally, true if Mocha should diff the expected and actual values.
 */
CallbackContext.prototype.assert = function (data) {
	this.framework._assert(this.sequenceIndex, this.requestType, data);
}

/**
 * Performs the questionMarkCheck on the response, and asserts if it fails.
 */
CallbackContext.prototype._questionMarkCheck = function (response) {
	var actualSay = response.response.outputSpeech ? response.response.outputSpeech.ssml : undefined;

	var hasQuestionMark = false;
	for (var i = 0; actualSay && i < actualSay.length; i++) {
		var c = actualSay[i];
		if (c == '?' || c == '\u055E' || c == '\u061F' || c == '\u2E2E' || c == '\uFF1F') {
			hasQuestionMark = true;
			break;
		}
	}
	if (response.response.shouldEndSession && hasQuestionMark) {
		this.assert(
			{
				message: "Possible Certification Problem: The response ends the session but contains a question mark."
			});
	}
	if (!response.response.shouldEndSession && !hasQuestionMark) {
		this.assert(
			{
				message: "Possible Certification Problem: The response keeps the session open but does not contain a question mark."
			});
	}
}

module.exports = {

	locale: "en-US",
	version: "1.0",

	//TODO: allow these to be enabled or disabled on a per-request basis
	extraFeatures: {
		/**
		 * If set, responses that end with question marks must not end the session,
		 * and those without question marks must end the session.
		 */
		questionMarkCheck: true,
	},

	/**
	 * Initializes necessary values before using the test framework.
	 * @param {object} index The object containing your skill's 'handler' method.
	 * @param {string} appId The Skill's app ID. Looks like "amzn1.ask.skill.00000000-0000-0000-0000-000000000000".
	 * @param {string} userId The Amazon User ID to test with. Looks like "amzn1.ask.account.LONG_STRING"
	 */
	initialize: function (index, appId, userId) {
		this.index = index;
		this.appId = appId;
		this.userId = userId;
	},

	/**
	 * Initializes i18n.
	 * @param {object} resources The 'resources' object to give to i18n.
	 */
	initializeI18N: function (resources) {
		this.i18n = require('i18next');
		var sprintf = require('i18next-sprintf-postprocessor');
		this.i18n.use(sprintf).init({
			overloadTranslationOptionHandler: sprintf.overloadTranslationOptionHandler,
			returnObjects: true,
			lng: this.locale,
			resources: resources
		});
	},

	/**
	 * Changes the locale used by i18n and to generate requests.
	 * @param {string} locale E.g. "en-US"
	 */
	setLocale: function (locale) {
		if (!locale) throw "'locale' argument must be provided.";
		this.locale = locale;
		if (this.i18n) this.i18n.changeLanguage(this.locale);
	},

	/**
	 * Enables or disables an optional testing feature.
	 * @param {string} key The key of the feature to enable.
	 * @param {boolean} enabled Whether the feature should be enabled.
	 */
	setExtraFeature: function (key, enabled) {
		if (this.extraFeatures[key] === undefined) {
			throw "Framework has no feature with key '" + key + "'.";
		}
		this.extraFeatures[key] = !!enabled;
	},

	/**
	 * Generates a launch request object.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 */
	getLaunchRequest: function (locale) {
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "LaunchRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
			}
		};
	},

	/**
	 * Generates an intent request object.
	 * @param {string} intentName The name of the intent to call.
	 * @param {object} slots Slot data to call the intent with.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 */
	getIntentRequest: function (intentName, slots, locale) {
		if (!slots) {
			slots = {};
		}
		else {
			for (var key in slots) slots[key] = { name: key, value: slots[key] };
		}
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "IntentRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
				"intent": { "name": intentName, "slots": slots }
			},
		};
	},

	/**
	 * Generates a sesson ended request object.
	 * @param {string} reason The reason the session was ended.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 * @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-standard-request-types-reference#sessionendedrequest
	 */
	getSessionEndedRequest: function (reason, locale) {
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "SessionEndedRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
				"reason": reason
				//TODO: support error
			}
		};
	},

	/**
	 * Tests the responses of a sequence of requests to the skill.
	 * @param {object[]} sequence An array of requests to test. Each element can have these properties:
	 * `request`: The request to run. Generate these with one of the above `getFooRequest` methods.
	 * `says`: Optional String. Tests that the speech output from the request is the string specified.
	 * `saysNothing`: Optional Boolean. If true, tests that the response has no speech output.
	 * `reprompts`: Optional String. Tests that the reprompt output from the request is the string specified.
	 * `repromptsNothing`: Optional Boolean. If true, tests that the response has no reprompt output.
	 * `shouldEndSession`: Optional Boolean. If true, tests that the response to the request ends or does not end the session.
	 * `saysCallback`: Optional Function. Recieves the speech from the response as a parameter. You can make custom checks against it using any assertion library you like.
	 * `callback`: Optional Function. Recieves the response object from the request as a parameter. You can make custom checks against the response using any assertion library you like in here.
	 */
	test: function (sequence) {
		if (!this.index) throw "The module is not initialized. You must call 'initialize' before calling 'test'.";
		if (!sequence) throw "'sequence' argument must be provided.";

		var index = this.index;
		var locale = this.locale;
		var self = this;

		it("returns the correct responses", function (done) {
			var run = function (handler, sequenceIndex, attributes) {
				if (sequenceIndex >= sequence.length) {
					// all requests were executed
					done();
				}
				else {
					var ctx = awsContext();
					var currentItem = sequence[sequenceIndex];<commit_msg>allow user to provide mocha test description<commit_after>
const awsContext = require('aws-lambda-mock-context');
const AssertionError = require('assertion-error');
const uuid = require('uuid');

CallbackContext = function (framework, sequenceIndex, locale, requestType) {
	this.framework = framework;
	this.sequenceIndex = sequenceIndex;
	this.locale = locale;
	this.requestType = requestType;
}

/**
 * Formats text via i18n, using the locale that was used for the request.
 */
CallbackContext.prototype.t = function (keys, params) {
	if (!params) params = {}
	if (!params.lng) params.lng = this.locale;
	return this.framework.t(keys, params);
}

/**
 * Throws an assertion error.
 * @param {object} data Data for the error. Can include:
 * `message`: Optionally, a message describing the failure.
 * `expected`: Optionally, the expected value.
 * `actual`: Optionally, the actual value.
 * `operator`: Optionally, the comparison operator that was used.
 * `showDiff`: Optionally, true if Mocha should diff the expected and actual values.
 */
CallbackContext.prototype.assert = function (data) {
	this.framework._assert(this.sequenceIndex, this.requestType, data);
}

/**
 * Performs the questionMarkCheck on the response, and asserts if it fails.
 */
CallbackContext.prototype._questionMarkCheck = function (response) {
	var actualSay = response.response.outputSpeech ? response.response.outputSpeech.ssml : undefined;

	var hasQuestionMark = false;
	for (var i = 0; actualSay && i < actualSay.length; i++) {
		var c = actualSay[i];
		if (c == '?' || c == '\u055E' || c == '\u061F' || c == '\u2E2E' || c == '\uFF1F') {
			hasQuestionMark = true;
			break;
		}
	}
	if (response.response.shouldEndSession && hasQuestionMark) {
		this.assert(
			{
				message: "Possible Certification Problem: The response ends the session but contains a question mark."
			});
	}
	if (!response.response.shouldEndSession && !hasQuestionMark) {
		this.assert(
			{
				message: "Possible Certification Problem: The response keeps the session open but does not contain a question mark."
			});
	}
}

module.exports = {

	locale: "en-US",
	version: "1.0",

	//TODO: allow these to be enabled or disabled on a per-request basis
	extraFeatures: {
		/**
		 * If set, responses that end with question marks must not end the session,
		 * and those without question marks must end the session.
		 */
		questionMarkCheck: true,
	},

	/**
	 * Initializes necessary values before using the test framework.
	 * @param {object} index The object containing your skill's 'handler' method.
	 * @param {string} appId The Skill's app ID. Looks like "amzn1.ask.skill.00000000-0000-0000-0000-000000000000".
	 * @param {string} userId The Amazon User ID to test with. Looks like "amzn1.ask.account.LONG_STRING"
	 */
	initialize: function (index, appId, userId) {
		this.index = index;
		this.appId = appId;
		this.userId = userId;
	},

	/**
	 * Initializes i18n.
	 * @param {object} resources The 'resources' object to give to i18n.
	 */
	initializeI18N: function (resources) {
		this.i18n = require('i18next');
		var sprintf = require('i18next-sprintf-postprocessor');
		this.i18n.use(sprintf).init({
			overloadTranslationOptionHandler: sprintf.overloadTranslationOptionHandler,
			returnObjects: true,
			lng: this.locale,
			resources: resources
		});
	},

	/**
	 * Changes the locale used by i18n and to generate requests.
	 * @param {string} locale E.g. "en-US"
	 */
	setLocale: function (locale) {
		if (!locale) throw "'locale' argument must be provided.";
		this.locale = locale;
		if (this.i18n) this.i18n.changeLanguage(this.locale);
	},

	/**
	 * Enables or disables an optional testing feature.
	 * @param {string} key The key of the feature to enable.
	 * @param {boolean} enabled Whether the feature should be enabled.
	 */
	setExtraFeature: function (key, enabled) {
		if (this.extraFeatures[key] === undefined) {
			throw "Framework has no feature with key '" + key + "'.";
		}
		this.extraFeatures[key] = !!enabled;
	},

	/**
	 * Generates a launch request object.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 */
	getLaunchRequest: function (locale) {
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "LaunchRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
			}
		};
	},

	/**
	 * Generates an intent request object.
	 * @param {string} intentName The name of the intent to call.
	 * @param {object} slots Slot data to call the intent with.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 */
	getIntentRequest: function (intentName, slots, locale) {
		if (!slots) {
			slots = {};
		}
		else {
			for (var key in slots) slots[key] = { name: key, value: slots[key] };
		}
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "IntentRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
				"intent": { "name": intentName, "slots": slots }
			},
		};
	},

	/**
	 * Generates a sesson ended request object.
	 * @param {string} reason The reason the session was ended.
	 * @param {string} locale Optional locale to use. If not specified, uses the locale specified by `setLocale`.
	 * @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-standard-request-types-reference#sessionendedrequest
	 */
	getSessionEndedRequest: function (reason, locale) {
		return {
			"version": this.version,
			"session": this._getSessionData(),
			"request": {
				"type": "SessionEndedRequest",
				"requestId": "EdwRequestId." + uuid.v4(),
				"timestamp": new Date().toISOString(),
				"locale": locale || this.locale,
				"reason": reason
				//TODO: support error
			}
		};
	},

	/**
	 * Tests the responses of a sequence of requests to the skill.
	 * @param {object[]} sequence An array of requests to test. Each element can have these properties:
	 * `request`: The request to run. Generate these with one of the above `getFooRequest` methods.
	 * `says`: Optional String. Tests that the speech output from the request is the string specified.
	 * `saysNothing`: Optional Boolean. If true, tests that the response has no speech output.
	 * `reprompts`: Optional String. Tests that the reprompt output from the request is the string specified.
	 * `repromptsNothing`: Optional Boolean. If true, tests that the response has no reprompt output.
	 * `shouldEndSession`: Optional Boolean. If true, tests that the response to the request ends or does not end the session.
	 * `saysCallback`: Optional Function. Recieves the speech from the response as a parameter. You can make custom checks against it using any assertion library you like.
	 * `callback`: Optional Function. Recieves the response object from the request as a parameter. You can make custom checks against the response using any assertion library you like in here.
	 */
	test: function(sequence, testDescription)	{
		if (!this.index) throw "The module is not initialized. You must call 'initialize' before calling 'test'.";
		if (!sequence) throw "'sequence' argument must be provided.";

		var index = this.index;
		var locale = this.locale;
		var self = this;

		it(testDescription || "returns the correct responses", function(done) {
			var run = function(handler, sequenceIndex, attributes) {
				if (sequenceIndex >= sequence.length) {
					// all requests were executed
					done();
				}
				else {
					var ctx = awsContext();
					var currentItem = sequence[sequenceIndex];
{'fraction_non_alphanumeric': 0.1227490607591657, 'fraction_numerical': 0.00919808265319342, 'mean_word_length': 3.7813564571074636, 'pattern_counts': {'":': 50, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1575078', 'n_tokens_mistral': 5022, 'n_tokens_neox': 4457, 'n_words': 1608}