botbuilder-js/libraries/botbuilder-dialogs-adaptive/tests/regexRecognizer.test.js

497 строки
22 KiB
JavaScript

/* eslint-disable @typescript-eslint/no-var-requires */
const assert = require('assert');
const { Recognizer } = require('botbuilder-dialogs');
const {
RegexRecognizer,
IntentPattern,
EntityRecognizerSet,
RegexEntityRecognizer,
AgeEntityRecognizer,
ConfirmationEntityRecognizer,
CurrencyEntityRecognizer,
DateTimeEntityRecognizer,
DimensionEntityRecognizer,
EmailEntityRecognizer,
GuidEntityRecognizer,
HashtagEntityRecognizer,
IpEntityRecognizer,
MentionEntityRecognizer,
NumberEntityRecognizer,
OrdinalEntityRecognizer,
PercentageEntityRecognizer,
PhoneNumberEntityRecognizer,
TemperatureEntityRecognizer,
UrlEntityRecognizer,
} = require('../');
const {
colorIntentText,
codeIntentText,
recognizeIntentAndValidateTelemetry,
recognizeIntentAndValidateTelemetry_withCustomActivity,
spyOnTelemetryClientTrackEvent,
} = require('./recognizerTelemetryUtils');
const { createContext, createMessageActivity } = require('./activityUtils');
const { validateCodeIntent, validateColorIntent } = require('./intentValidations');
const { Culture } = require('@microsoft/recognizers-text-suite');
describe('RegexRecognizer Tests', function () {
const recognizer = new RegexRecognizer().configure({
intents: [
new IntentPattern('codeIntent', '(?<code>[a-z][0-9])'),
new IntentPattern('colorIntent', '(color|colour)'),
],
entities: new EntityRecognizerSet(
new AgeEntityRecognizer(),
new ConfirmationEntityRecognizer(),
new CurrencyEntityRecognizer(),
new DateTimeEntityRecognizer(),
new DimensionEntityRecognizer(),
new EmailEntityRecognizer(),
new GuidEntityRecognizer(),
new HashtagEntityRecognizer(),
new IpEntityRecognizer(),
new MentionEntityRecognizer(),
new NumberEntityRecognizer(),
new OrdinalEntityRecognizer(),
new PercentageEntityRecognizer(),
new PhoneNumberEntityRecognizer(),
new TemperatureEntityRecognizer(),
new UrlEntityRecognizer(),
new RegexEntityRecognizer('color', '(red|green|blue|purple|orange|violet|white|black)'),
new RegexEntityRecognizer('backgroundColor', '(back|background) {color}'),
new RegexEntityRecognizer('foregroundColor', '(foreground|front) {color}')
),
});
const recognizerResultSample = {
text: 'textValue',
alteredText: 'alteredTextValue',
intents: {
intent1: { score: 0.03 },
intent2: { score: 0.95 },
intent3: { score: 0.02 },
},
entities: {
name: ['value1', 'value2', 'value3'],
$instance: {
name: [{ startIndex: 15 }],
},
},
additionalProperties: 'additionalPropertiesValue',
};
it('recognize regex pattern with dialog context', async function () {
const dc = createContext(codeIntentText);
const activity = dc.context.activity;
let result = await recognizer.recognize(dc, activity);
validateCodeIntent(result);
// verify seed text is not exposed
const entities = result.entities;
assert.strictEqual(entities.text, undefined);
assert(entities.code);
activity.text = colorIntentText;
result = await recognizer.recognize(dc, activity);
validateColorIntent(result);
});
it('recognize regex pattern with custom activity', async function () {
const dc = createContext('');
const activity = createMessageActivity(codeIntentText);
let result = await recognizer.recognize(dc, activity);
validateCodeIntent(result);
activity.text = 'I would like color red and orange';
result = await recognizer.recognize(dc, activity);
validateColorIntent(result);
});
describe('telemetry', function () {
let spy;
beforeEach(function () {
spy = spyOnTelemetryClientTrackEvent(recognizer);
});
afterEach(function () {
spy.restore();
});
it('logs PII when logPersonalInformation is true', async function () {
recognizer.logPersonalInformation = true;
await recognizeIntentAndValidateTelemetry({
text: codeIntentText,
callCount: 1,
recognizer,
spy,
});
await recognizeIntentAndValidateTelemetry({
text: colorIntentText,
callCount: 2,
recognizer,
spy,
});
await recognizeIntentAndValidateTelemetry_withCustomActivity({
text: codeIntentText,
callCount: 3,
recognizer,
spy,
});
});
it('does not log PII when logPersonalInformation is false', async function () {
recognizer.logPersonalInformation = false;
await recognizeIntentAndValidateTelemetry({
text: codeIntentText,
callCount: 1,
recognizer,
spy,
});
await recognizeIntentAndValidateTelemetry({
text: colorIntentText,
callCount: 2,
recognizer,
spy,
});
await recognizeIntentAndValidateTelemetry_withCustomActivity({
text: codeIntentText,
callCount: 3,
recognizer,
spy,
});
});
it('should refrain from logging PII by default', async function () {
const recognizerWithDefaultLogPii = new RegexRecognizer().configure({
intents: [
new IntentPattern('codeIntent', '(?<code>[a-z][0-9])'),
new IntentPattern('colorIntent', '(color|colour)'),
],
entities: new EntityRecognizerSet(
new NumberEntityRecognizer(),
new OrdinalEntityRecognizer(),
new RegexEntityRecognizer('color', '(red|green|blue|purple|orange|violet|white|black)'),
new RegexEntityRecognizer('backgroundColor', '(back|background) {color}'),
new RegexEntityRecognizer('foregroundColor', '(foreground|front) {color}')
),
});
const trackEventSpy = spyOnTelemetryClientTrackEvent(recognizerWithDefaultLogPii);
await recognizeIntentAndValidateTelemetry({
text: codeIntentText,
callCount: 1,
recognizer: recognizerWithDefaultLogPii,
spy: trackEventSpy,
});
await recognizeIntentAndValidateTelemetry_withCustomActivity({
text: codeIntentText,
callCount: 2,
recognizer: recognizerWithDefaultLogPii,
spy: trackEventSpy,
});
});
});
it('recognize regex pattern with text and locale', async function () {
const dc = createContext('');
const activity = createMessageActivity(codeIntentText);
activity.locale = Culture.English;
let result = await recognizer.recognize(dc, activity);
validateCodeIntent(result);
activity.text = 'I would like color red and orange';
result = await recognizer.recognize(dc, activity);
validateColorIntent(result);
});
it('recognize age', async function () {
const dc = createContext('');
const activity = createMessageActivity('I am 12 years old');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.age[0], '12 years old', 'should recognize age text');
const entity = entities['$instance'].age[0].resolution;
assert.strictEqual(entity.unit, 'Year', 'should recognize age unit');
assert.strictEqual(entity.value, '12', 'should recognize age value');
});
it('recognize confirmation', async function () {
const dc = createContext('');
const activity = createMessageActivity("It's OK");
let result = await recognizer.recognize(dc, activity);
let entities = result.entities;
assert.strictEqual(entities.boolean[0], 'OK', 'should recognize positive confirmation text');
let entity = entities['$instance'].boolean[0].resolution;
assert.strictEqual(entity.value, true, 'should recognize positive confirmation');
activity.text = "It's not OK";
result = await recognizer.recognize(dc, activity);
entities = result.entities;
assert.strictEqual(entities.boolean[0], 'not OK', 'should recognize negative confirmation text');
entity = entities['$instance'].boolean[0].resolution;
assert.strictEqual(entity.value, false, 'should recognize negative confirmation');
});
it('recognize currency', async function () {
const dc = createContext('');
const activity = createMessageActivity('Interest expense in the 1988 third quarter was $75.3 million');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.currency[0], '$75.3 million', 'should recognize currency text');
const entity = entities['$instance'].currency[0].resolution;
assert.strictEqual(entity.value, '75300000', 'should recognize currency value');
assert.strictEqual(entity.unit, 'Dollar', 'should recognize currency unit');
});
it('recognize datetime', async function () {
const dc = createContext('');
const activity = createMessageActivity('I will go back at 8pm today');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities['datetimeV2.datetime'][0], '8pm today', 'should recognize datetime text');
const entity = entities['$instance']['datetimeV2.datetime'][0].resolution;
const recognizedTime = new Date(entity.values[0].value).getTime();
const targetTime = new Date().setHours(20, 0, 0, 0);
assert.strictEqual(recognizedTime, targetTime, 'should recognize correct datetime');
});
it('recognize dimension', async function () {
const dc = createContext('');
const activity = createMessageActivity(
'The six-mile trip to my airport hotel that had taken 20 minutes earlier in the day took more than three hours.'
);
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.dimension[0], 'six-mile', 'should recognize dimension text');
const entity = entities['$instance'].dimension[0].resolution;
assert.strictEqual(entity.value, '6', 'should recognize dimension value');
assert.strictEqual(entity.unit, 'Mile', 'should recognize dimension unit');
});
it('recognize email address', async function () {
const dc = createContext('');
const activity = createMessageActivity('contact service@contoso.com');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.email[0], 'service@contoso.com', 'should recognize email address');
});
it('recognize guid', async function () {
const dc = createContext('');
const activity = createMessageActivity('the token is 21EC2020-3AEA-1069-A2DD-08002B30309D');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.guid[0], '21EC2020-3AEA-1069-A2DD-08002B30309D', 'should recognize guid');
});
it('recognize hashtag', async function () {
const dc = createContext('');
const activity = createMessageActivity('make #America great again #Trump');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.hashtag.length, 2, 'should recognize 2 hashtags');
assert.strictEqual(entities.hashtag[0], '#America', 'should recognize first hashtag');
assert.strictEqual(entities.hashtag[1], '#Trump', 'should recognize second hashtag');
});
it('recognize IP address', async function () {
const dc = createContext('');
const activity = createMessageActivity('gateway: 192.168.1.1');
let result = await recognizer.recognize(dc, activity);
let entities = result.entities;
assert.strictEqual(entities.ip[0], '192.168.1.1', 'should recognize IPv4 address');
activity.text = 'gateway: [2001:1d5f::1]';
result = await recognizer.recognize(dc, activity);
entities = result.entities;
assert.strictEqual(entities.ip[0], '2001:1d5f::1', 'should recognize IPv6 address');
activity.text = 'v4: 192.168.1.1 v6: 2001:1d5f::1';
result = await recognizer.recognize(dc, activity);
entities = result.entities;
assert.strictEqual(entities.ip.length, 2, 'should recognize hybrid IP addresses');
});
it('recognize mention', async function () {
const dc = createContext('');
const activity = createMessageActivity('make #America great again @realDonaldTrump');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.mention[0], '@realDonaldTrump', 'should recognize mention');
});
it('recognize number', async function () {
const dc = createContext('');
const activity = createMessageActivity('I have two apples');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.number[0], 'two', 'should recognize number text');
const entity = entities['$instance'].number[0].resolution;
assert.strictEqual(entity.value, '2', 'should recognize number value');
});
it('recognize ordinal number', async function () {
const dc = createContext('');
const activity = createMessageActivity('the 237th line');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.ordinal[0], '237th', 'should recognize ordinal number text');
const entity = entities['$instance'].ordinal[0].resolution;
assert.strictEqual(entity.value, '237', 'should recognize ordinal number value');
});
it('recognize percentage', async function () {
const dc = createContext('');
const activity = createMessageActivity('one hundred percents');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.percentage[0], 'one hundred percents', 'should recognize percentage text');
const entity = entities['$instance'].percentage[0].resolution;
assert.strictEqual(entity.value, '100%', 'should recognize percentage value');
});
it('recognize temperature', async function () {
const dc = createContext('');
const activity = createMessageActivity('the temperature is 21 °C');
let result = await recognizer.recognize(dc, activity);
let entities = result.entities;
assert.strictEqual(entities.temperature[0], '21 °C', 'should recognize celsius temperature text');
let entity = entities['$instance'].temperature[0].resolution;
assert.strictEqual(entity.value, '21', 'should recognize celsius temperature value');
assert.strictEqual(entity.unit, 'C', 'should recognize celsius temperature unit');
activity.text = 'the temperature is 70 °F';
result = await recognizer.recognize(dc, activity);
entities = result.entities;
assert.strictEqual(entities.temperature[0], '70 °F', 'should recognize fahrenheit temperature text');
entity = entities['$instance'].temperature[0].resolution;
assert.strictEqual(entity.value, '70', 'should recognize fahrenheit temperature value');
assert.strictEqual(entity.unit, 'F', 'should recognize fahrenheit temperature unit');
});
it('recognize url', async function () {
const dc = createContext('');
const activity = createMessageActivity('check out https://www.microsoft.com');
const result = await recognizer.recognize(dc, activity);
const entities = result.entities;
assert.strictEqual(entities.url[0], 'https://www.microsoft.com', 'should recognize url');
});
it('optional match groups', async function () {
const recognizer = new RegexRecognizer().configure({
intents: [
new IntentPattern('AddIntent', '(?:add|create) .*(?:to-do|todo|task)(?: )?(?:named (?<title>.*))?'),
],
});
const dc = createContext('');
let activity = createMessageActivity('add a todo named first');
let result = await recognizer.recognize(dc, activity);
assert.strictEqual(result.entities.title[0], 'first');
activity = createMessageActivity('add a todo');
result = await recognizer.recognize(dc, activity);
assert.strictEqual(result.entities.title, undefined);
});
it('basic telemetry test', function () {
const properties = {};
properties['test'] = 'testvalue';
properties['foo'] = 'foovalue';
const metrics = {};
const context = {};
const activity = {};
let callCount = 0;
const telemetryClient = {
trackEvent: (telemetry) => {
assert(telemetry, 'telemetry is null');
switch (++callCount) {
case 1:
assert.strictEqual(telemetry.name, 'TestResult');
assert(telemetry.properties);
assert('test' in telemetry.properties);
assert.strictEqual(telemetry.properties['test'], properties['test']);
assert('foo' in telemetry.properties);
assert.strictEqual(telemetry.properties['foo'], properties['foo']);
assert('MyImportantProperty' in telemetry.properties);
assert.strictEqual(telemetry.properties['MyImportantProperty'], 'myImportantValue');
break;
case 2:
assert.strictEqual(telemetry.name, 'MySecondEvent');
assert(telemetry.properties);
assert('MyImportantProperty2' in telemetry.properties);
assert.strictEqual(telemetry.properties['MyImportantProperty2'], 'myImportantValue2');
break;
default:
console.log('Call number:' + callCount);
console.log(telemetry);
assert(false);
break;
}
},
};
const recognizer = new MockTelemetryInRecognizer();
recognizer.telemetryClient = telemetryClient;
recognizer.recognize(context, activity, properties, metrics);
});
it('check fillRecognizerResultTelemetryProperties', function () {
const recognizer = new Recognizer();
const telemetryProperties = {
TelemetryPropertiesKey1: 'TelemetryPropertiesValue1',
TelemetryPropertiesKey2: 'TelemetryPropertiesValue2',
};
const properties = recognizer.fillRecognizerResultTelemetryProperties(
recognizerResultSample,
telemetryProperties
);
assert('Text' in properties);
assert.strictEqual(properties['Text'], recognizerResultSample['text']);
assert('AlteredText' in properties);
assert.strictEqual(properties['AlteredText'], recognizerResultSample['alteredText']);
assert('TopIntent' in properties);
assert.strictEqual(properties['TopIntent'], 'intent2');
assert('TopIntentScore' in properties);
assert.strictEqual(properties['TopIntentScore'], '0.95');
assert('Intents' in properties);
assert.strictEqual(properties['Intents'], JSON.stringify(recognizerResultSample['intents']));
assert('Entities' in properties);
assert.strictEqual(properties['Entities'], JSON.stringify(recognizerResultSample['entities']));
assert('AdditionalProperties' in properties);
assert.strictEqual(properties['AdditionalProperties'], '{"additionalProperties":"additionalPropertiesValue"}');
assert('TelemetryPropertiesKey1' in properties);
assert.strictEqual(properties['TelemetryPropertiesKey1'], telemetryProperties['TelemetryPropertiesKey1']);
assert('TelemetryPropertiesKey2' in properties);
assert.strictEqual(properties['TelemetryPropertiesKey2'], telemetryProperties['TelemetryPropertiesKey2']);
});
});
class MockTelemetryInRecognizer extends Recognizer {
recognize(dialogContext, activity, properties, metrics) {
if (!('MyImportantProperty' in properties)) {
properties['MyImportantProperty'] = 'myImportantValue';
}
this.telemetryClient.trackEvent({
name: 'TestResult',
properties: properties,
metrics: metrics,
});
// Create second event
const secondProperties = {};
secondProperties['MyImportantProperty2'] = 'myImportantValue2';
this.telemetryClient.trackEvent({
name: 'MySecondEvent',
properties: secondProperties,
});
}
}