matchers.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.toBeAttached = toBeAttached;
  6. exports.toBeChecked = toBeChecked;
  7. exports.toBeDisabled = toBeDisabled;
  8. exports.toBeEditable = toBeEditable;
  9. exports.toBeEmpty = toBeEmpty;
  10. exports.toBeEnabled = toBeEnabled;
  11. exports.toBeFocused = toBeFocused;
  12. exports.toBeHidden = toBeHidden;
  13. exports.toBeInViewport = toBeInViewport;
  14. exports.toBeOK = toBeOK;
  15. exports.toBeVisible = toBeVisible;
  16. exports.toContainText = toContainText;
  17. exports.toHaveAttribute = toHaveAttribute;
  18. exports.toHaveCSS = toHaveCSS;
  19. exports.toHaveClass = toHaveClass;
  20. exports.toHaveCount = toHaveCount;
  21. exports.toHaveId = toHaveId;
  22. exports.toHaveJSProperty = toHaveJSProperty;
  23. exports.toHaveText = toHaveText;
  24. exports.toHaveTitle = toHaveTitle;
  25. exports.toHaveURL = toHaveURL;
  26. exports.toHaveValue = toHaveValue;
  27. exports.toHaveValues = toHaveValues;
  28. exports.toPass = toPass;
  29. var _utilsBundle = require("playwright-core/lib/utilsBundle");
  30. var _util = require("../util");
  31. var _toBeTruthy = require("./toBeTruthy");
  32. var _toEqual = require("./toEqual");
  33. var _toMatchText = require("./toMatchText");
  34. var _utils = require("playwright-core/lib/utils");
  35. var _globals = require("../common/globals");
  36. var _testInfo = require("../worker/testInfo");
  37. /**
  38. * Copyright Microsoft Corporation. All rights reserved.
  39. *
  40. * Licensed under the Apache License, Version 2.0 (the "License");
  41. * you may not use this file except in compliance with the License.
  42. * You may obtain a copy of the License at
  43. *
  44. * http://www.apache.org/licenses/LICENSE-2.0
  45. *
  46. * Unless required by applicable law or agreed to in writing, software
  47. * distributed under the License is distributed on an "AS IS" BASIS,
  48. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  49. * See the License for the specific language governing permissions and
  50. * limitations under the License.
  51. */
  52. function toBeAttached(locator, options) {
  53. const attached = !options || options.attached === undefined || options.attached === true;
  54. const expected = attached ? 'attached' : 'detached';
  55. const unexpected = attached ? 'detached' : 'attached';
  56. const arg = attached ? '' : '{ attached: false }';
  57. return _toBeTruthy.toBeTruthy.call(this, 'toBeAttached', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
  58. return await locator._expect(attached ? 'to.be.attached' : 'to.be.detached', {
  59. isNot,
  60. timeout
  61. });
  62. }, options);
  63. }
  64. function toBeChecked(locator, options) {
  65. const checked = !options || options.checked === undefined || options.checked === true;
  66. const expected = checked ? 'checked' : 'unchecked';
  67. const unexpected = checked ? 'unchecked' : 'checked';
  68. const arg = checked ? '' : '{ checked: false }';
  69. return _toBeTruthy.toBeTruthy.call(this, 'toBeChecked', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
  70. return await locator._expect(checked ? 'to.be.checked' : 'to.be.unchecked', {
  71. isNot,
  72. timeout
  73. });
  74. }, options);
  75. }
  76. function toBeDisabled(locator, options) {
  77. return _toBeTruthy.toBeTruthy.call(this, 'toBeDisabled', locator, 'Locator', 'disabled', 'enabled', '', async (isNot, timeout) => {
  78. return await locator._expect('to.be.disabled', {
  79. isNot,
  80. timeout
  81. });
  82. }, options);
  83. }
  84. function toBeEditable(locator, options) {
  85. const editable = !options || options.editable === undefined || options.editable === true;
  86. const expected = editable ? 'editable' : 'readOnly';
  87. const unexpected = editable ? 'readOnly' : 'editable';
  88. const arg = editable ? '' : '{ editable: false }';
  89. return _toBeTruthy.toBeTruthy.call(this, 'toBeEditable', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
  90. return await locator._expect(editable ? 'to.be.editable' : 'to.be.readonly', {
  91. isNot,
  92. timeout
  93. });
  94. }, options);
  95. }
  96. function toBeEmpty(locator, options) {
  97. return _toBeTruthy.toBeTruthy.call(this, 'toBeEmpty', locator, 'Locator', 'empty', 'notEmpty', '', async (isNot, timeout) => {
  98. return await locator._expect('to.be.empty', {
  99. isNot,
  100. timeout
  101. });
  102. }, options);
  103. }
  104. function toBeEnabled(locator, options) {
  105. const enabled = !options || options.enabled === undefined || options.enabled === true;
  106. const expected = enabled ? 'enabled' : 'disabled';
  107. const unexpected = enabled ? 'disabled' : 'enabled';
  108. const arg = enabled ? '' : '{ enabled: false }';
  109. return _toBeTruthy.toBeTruthy.call(this, 'toBeEnabled', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
  110. return await locator._expect(enabled ? 'to.be.enabled' : 'to.be.disabled', {
  111. isNot,
  112. timeout
  113. });
  114. }, options);
  115. }
  116. function toBeFocused(locator, options) {
  117. return _toBeTruthy.toBeTruthy.call(this, 'toBeFocused', locator, 'Locator', 'focused', 'inactive', '', async (isNot, timeout) => {
  118. return await locator._expect('to.be.focused', {
  119. isNot,
  120. timeout
  121. });
  122. }, options);
  123. }
  124. function toBeHidden(locator, options) {
  125. return _toBeTruthy.toBeTruthy.call(this, 'toBeHidden', locator, 'Locator', 'hidden', 'visible', '', async (isNot, timeout) => {
  126. return await locator._expect('to.be.hidden', {
  127. isNot,
  128. timeout
  129. });
  130. }, options);
  131. }
  132. function toBeVisible(locator, options) {
  133. const visible = !options || options.visible === undefined || options.visible === true;
  134. const expected = visible ? 'visible' : 'hidden';
  135. const unexpected = visible ? 'hidden' : 'visible';
  136. const arg = visible ? '' : '{ visible: false }';
  137. return _toBeTruthy.toBeTruthy.call(this, 'toBeVisible', locator, 'Locator', expected, unexpected, arg, async (isNot, timeout) => {
  138. return await locator._expect(visible ? 'to.be.visible' : 'to.be.hidden', {
  139. isNot,
  140. timeout
  141. });
  142. }, options);
  143. }
  144. function toBeInViewport(locator, options) {
  145. return _toBeTruthy.toBeTruthy.call(this, 'toBeInViewport', locator, 'Locator', 'in viewport', 'outside viewport', '', async (isNot, timeout) => {
  146. return await locator._expect('to.be.in.viewport', {
  147. isNot,
  148. expectedNumber: options === null || options === void 0 ? void 0 : options.ratio,
  149. timeout
  150. });
  151. }, options);
  152. }
  153. function toContainText(locator, expected, options = {}) {
  154. if (Array.isArray(expected)) {
  155. return _toEqual.toEqual.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
  156. const expectedText = (0, _toMatchText.toExpectedTextValues)(expected, {
  157. matchSubstring: true,
  158. normalizeWhiteSpace: true,
  159. ignoreCase: options.ignoreCase
  160. });
  161. return await locator._expect('to.contain.text.array', {
  162. expectedText,
  163. isNot,
  164. useInnerText: options.useInnerText,
  165. timeout
  166. });
  167. }, expected, {
  168. ...options,
  169. contains: true
  170. });
  171. } else {
  172. return _toMatchText.toMatchText.call(this, 'toContainText', locator, 'Locator', async (isNot, timeout) => {
  173. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected], {
  174. matchSubstring: true,
  175. normalizeWhiteSpace: true,
  176. ignoreCase: options.ignoreCase
  177. });
  178. return await locator._expect('to.have.text', {
  179. expectedText,
  180. isNot,
  181. useInnerText: options.useInnerText,
  182. timeout
  183. });
  184. }, expected, options);
  185. }
  186. }
  187. function toHaveAttribute(locator, name, expected, options) {
  188. if (!options) {
  189. // Update params for the case toHaveAttribute(name, options);
  190. if (typeof expected === 'object' && !(0, _utils.isRegExp)(expected)) {
  191. options = expected;
  192. expected = undefined;
  193. }
  194. }
  195. if (expected === undefined) {
  196. return _toBeTruthy.toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', 'have attribute', 'not have attribute', '', async (isNot, timeout) => {
  197. return await locator._expect('to.have.attribute', {
  198. expressionArg: name,
  199. isNot,
  200. timeout
  201. });
  202. }, options);
  203. }
  204. return _toMatchText.toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout) => {
  205. var _options;
  206. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected], {
  207. ignoreCase: (_options = options) === null || _options === void 0 ? void 0 : _options.ignoreCase
  208. });
  209. return await locator._expect('to.have.attribute.value', {
  210. expressionArg: name,
  211. expectedText,
  212. isNot,
  213. timeout
  214. });
  215. }, expected, options);
  216. }
  217. function toHaveClass(locator, expected, options) {
  218. if (Array.isArray(expected)) {
  219. return _toEqual.toEqual.call(this, 'toHaveClass', locator, 'Locator', async (isNot, timeout) => {
  220. const expectedText = (0, _toMatchText.toExpectedTextValues)(expected);
  221. return await locator._expect('to.have.class.array', {
  222. expectedText,
  223. isNot,
  224. timeout
  225. });
  226. }, expected, options);
  227. } else {
  228. return _toMatchText.toMatchText.call(this, 'toHaveClass', locator, 'Locator', async (isNot, timeout) => {
  229. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected]);
  230. return await locator._expect('to.have.class', {
  231. expectedText,
  232. isNot,
  233. timeout
  234. });
  235. }, expected, options);
  236. }
  237. }
  238. function toHaveCount(locator, expected, options) {
  239. return _toEqual.toEqual.call(this, 'toHaveCount', locator, 'Locator', async (isNot, timeout) => {
  240. return await locator._expect('to.have.count', {
  241. expectedNumber: expected,
  242. isNot,
  243. timeout
  244. });
  245. }, expected, options);
  246. }
  247. function toHaveCSS(locator, name, expected, options) {
  248. return _toMatchText.toMatchText.call(this, 'toHaveCSS', locator, 'Locator', async (isNot, timeout) => {
  249. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected]);
  250. return await locator._expect('to.have.css', {
  251. expressionArg: name,
  252. expectedText,
  253. isNot,
  254. timeout
  255. });
  256. }, expected, options);
  257. }
  258. function toHaveId(locator, expected, options) {
  259. return _toMatchText.toMatchText.call(this, 'toHaveId', locator, 'Locator', async (isNot, timeout) => {
  260. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected]);
  261. return await locator._expect('to.have.id', {
  262. expectedText,
  263. isNot,
  264. timeout
  265. });
  266. }, expected, options);
  267. }
  268. function toHaveJSProperty(locator, name, expected, options) {
  269. return _toEqual.toEqual.call(this, 'toHaveJSProperty', locator, 'Locator', async (isNot, timeout) => {
  270. return await locator._expect('to.have.property', {
  271. expressionArg: name,
  272. expectedValue: expected,
  273. isNot,
  274. timeout
  275. });
  276. }, expected, options);
  277. }
  278. function toHaveText(locator, expected, options = {}) {
  279. if (Array.isArray(expected)) {
  280. return _toEqual.toEqual.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
  281. const expectedText = (0, _toMatchText.toExpectedTextValues)(expected, {
  282. normalizeWhiteSpace: true,
  283. ignoreCase: options.ignoreCase
  284. });
  285. return await locator._expect('to.have.text.array', {
  286. expectedText,
  287. isNot,
  288. useInnerText: options === null || options === void 0 ? void 0 : options.useInnerText,
  289. timeout
  290. });
  291. }, expected, options);
  292. } else {
  293. return _toMatchText.toMatchText.call(this, 'toHaveText', locator, 'Locator', async (isNot, timeout) => {
  294. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected], {
  295. normalizeWhiteSpace: true,
  296. ignoreCase: options.ignoreCase
  297. });
  298. return await locator._expect('to.have.text', {
  299. expectedText,
  300. isNot,
  301. useInnerText: options === null || options === void 0 ? void 0 : options.useInnerText,
  302. timeout
  303. });
  304. }, expected, options);
  305. }
  306. }
  307. function toHaveValue(locator, expected, options) {
  308. return _toMatchText.toMatchText.call(this, 'toHaveValue', locator, 'Locator', async (isNot, timeout) => {
  309. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected]);
  310. return await locator._expect('to.have.value', {
  311. expectedText,
  312. isNot,
  313. timeout
  314. });
  315. }, expected, options);
  316. }
  317. function toHaveValues(locator, expected, options) {
  318. return _toEqual.toEqual.call(this, 'toHaveValues', locator, 'Locator', async (isNot, timeout) => {
  319. const expectedText = (0, _toMatchText.toExpectedTextValues)(expected);
  320. return await locator._expect('to.have.values', {
  321. expectedText,
  322. isNot,
  323. timeout
  324. });
  325. }, expected, options);
  326. }
  327. function toHaveTitle(page, expected, options = {}) {
  328. const locator = page.locator(':root');
  329. return _toMatchText.toMatchText.call(this, 'toHaveTitle', locator, 'Locator', async (isNot, timeout) => {
  330. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected], {
  331. normalizeWhiteSpace: true
  332. });
  333. return await locator._expect('to.have.title', {
  334. expectedText,
  335. isNot,
  336. timeout
  337. });
  338. }, expected, options);
  339. }
  340. function toHaveURL(page, expected, options) {
  341. const baseURL = page.context()._options.baseURL;
  342. expected = typeof expected === 'string' ? (0, _utils.constructURLBasedOnBaseURL)(baseURL, expected) : expected;
  343. const locator = page.locator(':root');
  344. return _toMatchText.toMatchText.call(this, 'toHaveURL', locator, 'Locator', async (isNot, timeout) => {
  345. const expectedText = (0, _toMatchText.toExpectedTextValues)([expected]);
  346. return await locator._expect('to.have.url', {
  347. expectedText,
  348. isNot,
  349. timeout
  350. });
  351. }, expected, options);
  352. }
  353. async function toBeOK(response) {
  354. const matcherName = 'toBeOK';
  355. (0, _util.expectTypes)(response, ['APIResponse'], matcherName);
  356. const contentType = response.headers()['content-type'];
  357. const isTextEncoding = contentType && (0, _utils.isTextualMimeType)(contentType);
  358. const [log, text] = this.isNot === response.ok() ? await Promise.all([response._fetchLog(), isTextEncoding ? response.text() : null]) : [];
  359. const message = () => this.utils.matcherHint(matcherName, undefined, '', {
  360. isNot: this.isNot
  361. }) + (0, _util.callLogText)(log) + (text === null ? '' : `\nResponse text:\n${_utilsBundle.colors.dim((text === null || text === void 0 ? void 0 : text.substring(0, 1000)) || '')}`);
  362. const pass = response.ok();
  363. return {
  364. message,
  365. pass
  366. };
  367. }
  368. async function toPass(callback, options = {}) {
  369. const testInfo = (0, _globals.currentTestInfo)();
  370. const timeout = options.timeout !== undefined ? options.timeout : 0;
  371. const {
  372. deadline,
  373. timeoutMessage
  374. } = testInfo ? testInfo._deadlineForMatcher(timeout) : _testInfo.TestInfoImpl._defaultDeadlineForMatcher(timeout);
  375. const result = await (0, _utils.pollAgainstDeadline)(async () => {
  376. if (testInfo && (0, _globals.currentTestInfo)() !== testInfo) return {
  377. continuePolling: false,
  378. result: undefined
  379. };
  380. try {
  381. await callback();
  382. return {
  383. continuePolling: !!this.isNot,
  384. result: undefined
  385. };
  386. } catch (e) {
  387. return {
  388. continuePolling: !this.isNot,
  389. result: e
  390. };
  391. }
  392. }, deadline, options.intervals || [100, 250, 500, 1000]);
  393. if (result.timedOut) {
  394. const message = result.result ? [result.result.message, '', `Call Log:`, `- ${timeoutMessage}`].join('\n') : timeoutMessage;
  395. return {
  396. message: () => message,
  397. pass: !!this.isNot
  398. };
  399. }
  400. return {
  401. pass: !this.isNot,
  402. message: () => ''
  403. };
  404. }