typescript Cypress -验证别名的存在或将别名中的文本与“if”语句中的字符串进行比较

j7dteeu8  于 2023-11-20  发布在  TypeScript
关注(0)|答案(4)|浏览(196)

我正在用Cypress创建一个API自动化套件。
我有不需要登录的API测试,也有需要登录的测试。
我已经写了一个名为“callAPI”的实用方法,我所有的测试都会使用它-它有一堆错误检查,并根据调用者传递到方法中的内容自动使用相关的头/授权令牌来增强调用。
我还有一个'logIn'方法,它创建一个存储授权令牌的别名。
在自动化工程师调用callAPI来执行需要登录的API请求的场景中,我希望能够检测到他们没有登录,并抛出一条错误消息。
方法1:如果别名'accessToken'不存在,抛出错误。
问题:cy.state('aliases')的使用似乎已被弃用。
方法2:在'before'钩子中创建别名,默认值为'accessToken not set'.

before(() => {
    cy.wrap('accessToken not set').as('accessToken');
});

字符串
然后在callAPI中,检查别名的值是否等于'accessToken not set'并抛出错误。
问题:

if (useAccessToken && cy.get('@accessToken') == 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.');
}

if (useAccessToken && cy.get('@accessToken').invoke('text') == 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.');
}


第一个“if”语句显示“此比较似乎是无意的,因为类型”Chainable“和”string“没有重叠。”
第二个“if”语句显示“此比较似乎是无意的,因为类型”Chainable“和”string“没有重叠。”
我可能错误地认为在“if”语句中比较别名中的文本应该是微不足道的,但显然这不是一件事.?
方法3:这里的意图是在“当时”内进行比较。

cy.get('@accessToken').then(data => {
    cy.log(data.text());
});


问题:“data.text不是函数”
方法4:在我的“if”语句中,Assert别名的文本等于“accessToken not set”。

if (useAccessToken && cy.get('@accessToken').should('equal', 'accessToken not set')) {
    throw new Error('callAPI() - access token not set. Log in first.');
}


问题:如果此Assert失败,Cypress将抛出自己的错误,从而使抛出自定义错误的对象失效。
我的代码:

const apiURL = Cypress.env('apiUrl');

export const APIURLs = {
    Login: `${apiURL}access/login`,
    Info: `${apiURL}info`,
    InfoHealth: `${apiURL}info/health`
};

export class Common {
    logInAndSetAccessToken(emailAddress: string, password: string) {
        this.callAPI({
            requestObject: {
                method: 'POST',
                url: APIURLs.Login,
                body: {
                    email: emailAddress,
                    password: password
                }
            }, useAccessToken: false
        }).then(response => {
            cy.wrap(response.body.tokens.accessToken).as('accessToken');
        });
    }

    /**
     * 'headers' property, along with the following headers are automatically added to requestObject:
     * 
     * 'Content-Type': 'application/json; charset=utf-8'
     * 
     * 'Authorization': `Bearer ${accessToken}`
     */
    callAPI({ url = '', requestObject = {}, useAccessToken = true } = {}) {
        if (url.length > 3 && Object.keys(requestObject).length > 0) {
            throw new Error('callAPI() - method call ambigious. Pass url or requestObject, not both.');
        }

        if (url.length < 4 && Object.keys(requestObject).length == 0) {
            throw new Error('callAPI() - method call missing necessary information to make API call. Pass url or requestObject.');
        }

        if (useAccessToken && cy.get('@accessToken') == 'accessToken not set') { // This comparison appears to be unintentional because the types 'Chainable<JQuery<HTMLElement>>' and 'string' have no overlap.
            throw new Error('callAPI() - access token not set. Log in first.');
        }

        if (Object.keys(requestObject).length > 0) {
            if (!requestObject.method || !requestObject.url || !requestObject.body) {
                throw new Error('callAPI() - method, url or body properties are missing in the requestObject.');
            }

            if (!requestObject.headers) {
                Object.assign(requestObject, { headers: {} });
            }

            if (!requestObject.headers['Content-Type']) {
                Object.assign(requestObject.headers, { 'Content-Type': 'application/json; charset=utf-8' });
            }

            if (useAccessToken && !requestObject.headers['Authorization']) {
                Object.assign(requestObject.headers, { 'Authorization': `Bearer ${cy.get('@accessToken')}` });
            }

            return cy.request(requestObject);
        } else {
            if (url.length < 4) {
                throw new Error('callAPI() - invalid url, cannot call API.');
            }

            return cy.request(url);
        }
    }
}


除非我忽略了一些显而易见的问题(这并不奇怪),否则有什么方法可以解决这个问题吗?或者我应该仅仅依靠API来通知自动化工程师他们需要登录?
我使用TypeScript,如果这很重要。
谢谢你能提供的任何帮助。

vbopmzt1

vbopmzt11#

你的大多数方法都可以结合每种方法的最佳部分。

then和自定义错误内的比较

合并结合你的方法,你有一些工作

cy.wrap('some-token').as('accessToken')
cy.get('@accessToken').then(data => {
  if(data === 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.')
  }
})

cy.wrap('accessToken not set').as('accessToken')
cy.get('@accessToken').then(data => {
  if(data === 'accessToken not set') {
    throw new Error('callAPI() - access token not set. Log in first.')
  }
})

字符串


的数据

使用assert()自定义消息

您可以使用assert和自定义消息来执行类似的操作

function aliasIsSet(data) {
  const isSet = (data !== 'accessToken not set')
  const msg = isSet ? 'access token is set'
    :'callAPI() - access token not set. Log in first.' 
  assert(isSet, msg, {log:!isSet})
}

cy.wrap('some-token').as('accessToken')
cy.get('@accessToken').then(aliasIsSet)

cy.wrap('accessToken not set').as('accessToken')
cy.get('@accessToken').then(aliasIsSet)


k97glaaz

k97glaaz2#

您可以采取主动方法,在未设置令牌时进行调用。
这里我在构造函数中设置了别名,使它总是有一个值,这样Cypress在调用cy.get('@accessToken')时就不会中断。

export class Common {

    constructor() {
      cy.wrap('accessToken not set').as('accessToken')
    }

    logInAndSetAccessToken(emailAddress: string, password: string) {
        this.callAPI({
          ...
          }, useAccessToken: false
        }).then(response => {
            cy.wrap(response.body.tokens.accessToken).as('accessToken');
        });
    }

    callAPI({ url = '', requestObject = {}, useAccessToken = true } = {}) {

      cy.get('@accessToken').then(data => {
        if(useAccessToken && data === 'accessToken not set') {
          logInAndSetAccessToken(emailAddress, password)
        }
      })

      if (url.length > 3 && Object.keys(requestObject).length > 0) {

      ... rest of this method

字符串
或者,可以通过使用类属性来完全避免别名问题

export class Common {

    logInAndSetAccessToken(emailAddress: string, password: string) {
        this.callAPI({
          ...
          }, useAccessToken: false
        }).then(response => {
            this.token = response.body.tokens.accessToken;
        });
    }

    callAPI({ url = '', requestObject = {}, useAccessToken = true } = {}) {

        if(useAccessToken && !this.token) {
          logInAndSetAccessToken(emailAddress, password)
        }

        if (url.length > 3 && Object.keys(requestObject).length > 0) {

        ... rest of this method

zpf6vheq

zpf6vheq3#

1.在if语句本身中直接使用cy.get('@alias')值是不可能的,因为cy.get()会产生Chainable<Any>类型。因此,比较cy.get('@alias')cy.get('@alias').invoke('text')会产生Chainable元素,而不是字符串。
1.别名是在每个测试的基础上重置的,所以如果你想在测试之前设置别名,你需要在beforeEach()而不是before()中这样做。

  1. cy.get()命令包含一个隐式Assert,表示引用存在,这意味着如果没有找到cy.get('@alias'),Cypress将无法通过测试。
    1.幸运的是,我们不需要使用cy.get()来检索别名值-我们可以使用Mocha的共享上下文通过this.alias引用值。
    因此,考虑到所有这些,一个潜在的解决方案可能是:
describe('tests', () => {
  beforeEach(() => {
    cy.wrap('accessToken not set').as('accessToken');
  });

  it('tests something', function () {
    if (useAccessToken && this.accessToken == 'accessToken not set') {
      throw new Error('callAPI() - access token not set. Log in first.');
    }
  });
});

字符串
不幸的是,使用this.*需要在具有Mocha共享上下文对象的对象中调用该功能,而您的helper函数似乎没有该上下文。
Instead, we could use a Cypress environment variable,允许同步检查 * 和 *,返回一个字符串变量进行计算。

logInAndSetAccessToken(emailAddress: string, password: string) {
        this.callAPI({
            requestObject: {
                method: 'POST',
                url: APIURLs.Login,
                body: {
                    email: emailAddress,
                    password: password
                }
            }, useAccessToken: false
        }).then(response => {
            Cypress.env('accessToken', response.body.tokens.accessToken)
        });
    }

...

if (useAccessToken && Cypress.env('accessToken') == 'accessToken not set') { // This comparison appears to be unintentional because the types 'Chainable<JQuery<HTMLElement>>' and 'string' have no overlap.
            throw new Error('callAPI() - access token not set. Log in first.');
        }


注意:Cypress环境变量只在每个规范的基础上重置,因此访问令牌将在同一个规范文件中从测试到测试。如果你想避免这种情况,你可以在你的beforeEach()Cypress.env('accessToken', 'accessToken not set');)中设置环境变量的“默认”值。

55ooxyrt

55ooxyrt4#

感谢大家的帮助。
我最终将logIn方法和callAPI方法分开,如果需要使用访问令牌,则在callAPI中调用logIn方法。

export class Common {
    /**
     * Strictly for testing logging in - DOES NOT save access token for future calls.
     */
    logIn(emailAddress: string, password: string) {
        return cy.request({
            method: 'POST',
            url: APIURLs.AccessLogin,
            body: {
                email: emailAddress,
                password: password
            }
        });
    }

    /**
     * For API calls that require logging in/access token, pass userObject.
     */
    callAPI({ url = '', requestObject = {}, useAccessToken = true, userObject = {} } = {}) {
        if (url.length > 0 && Object.keys(requestObject).length > 0) {
            throw new Error('callAPI() - method call ambigious. Pass url or requestObject, not both.');
        }

        if (url.length < (apiURL.length + 4) && Object.keys(requestObject).length == 0) {
            throw new Error('callAPI() - method call missing necessary information to make API call. Pass url or requestObject.');
        }

        if (useAccessToken && Object.keys(userObject).length == 0) {
            throw new Error('callAPI() - cannot use access token without passing a userObject from configuration.');
        }

        if (Object.keys(requestObject).length > 0) {
            if (!requestObject.method || !requestObject.url || !requestObject.body) {
                throw new Error('callAPI() - method, url or body properties are missing in the requestObject.');
            }

            if (!requestObject.headers) {
                Object.assign(requestObject, { headers: {} });
            }

            if (!requestObject.headers['Content-Type']) {
                Object.assign(requestObject.headers, { 'Content-Type': 'application/json; charset=utf-8' });
            }
        } else {
            if (url.length < (apiURL.length + 4)) {
                throw new Error('callAPI() - invalid url, cannot call API.');
            }
        }

        if (useAccessToken) {
            return this.logIn(userObject.Email, userObject.m_UIPassword).then(response => {
                const accessToken = response.body.tokens.accessToken;

                if (!requestObject.headers['Authorization']) {
                    Object.assign(requestObject.headers, { 'Authorization': `Bearer ${accessToken}` });
                } else {
                    requestObject.headers['Authorization'] = `Bearer ${accessToken}`;
                }

                return cy.request(requestObject);
            });
        } else {
            if (Object.keys(requestObject).length > 0) {
                return cy.request(requestObject);
            } else {
                return cy.request(url);
            }
        }
    }
}

字符串

相关问题