Skip to content

Commit b8be23f

Browse files
committed
fix: add ios appium tests
1 parent dadb9bc commit b8be23f

File tree

4 files changed

+264
-1
lines changed

4 files changed

+264
-1
lines changed

Diff for: .github/workflows/appiumV2.yml renamed to .github/workflows/appiumV2_Android.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Appium V2 Tests
1+
name: Appium V2 Tests - Android
22

33
on:
44
push:

Diff for: .github/workflows/appiumV2_iOS.yml

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Appium V2 Tests - iOS
2+
3+
on:
4+
push:
5+
branches:
6+
- 3.x
7+
- appium-v1-deprecation
8+
9+
env:
10+
CI: true
11+
# Force terminal colors. @see https://www.npmjs.com/package/colors
12+
FORCE_COLOR: 1
13+
14+
jobs:
15+
appium1:
16+
runs-on: ubuntu-20.04
17+
18+
strategy:
19+
matrix:
20+
node-version: [16.x]
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
- name: Use Node.js ${{ matrix.node-version }}
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
- run: npm install --legacy-peer-deps
29+
env:
30+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
31+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
32+
- run: 'npm run test:ios:appium-quick'
33+
env: # Or as an environment variable
34+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
35+
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
36+
37+
38+
appium2:
39+
40+
runs-on: ubuntu-20.04
41+
42+
strategy:
43+
matrix:
44+
node-version: [16.x]
45+
46+
steps:
47+
- uses: actions/checkout@v4
48+
- name: Use Node.js ${{ matrix.node-version }}
49+
uses: actions/setup-node@v4
50+
with:
51+
node-version: ${{ matrix.node-version }}
52+
- run: npm install --legacy-peer-deps
53+
env:
54+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: true
55+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true
56+
- run: 'npm run test:ios:appium-other'
57+
env: # Or as an environment variable
58+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
59+
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}

Diff for: package.json

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
"test": "npm run test:unit && npm run test:runner",
4646
"test:appium-quick": "mocha test/helper/AppiumV2_test.js --grep 'quick'",
4747
"test:appium-other": "mocha test/helper/AppiumV2_test.js --grep 'second'",
48+
"test:ios:appium-quick": "mocha test/helper/AppiumV2_ios_test.js --grep 'quick'",
49+
"test:ios:appium-other": "mocha test/helper/AppiumV2_ios_test.js --grep 'second'",
4850
"test-app:start": "php -S 127.0.0.1:8000 -t test/data/app",
4951
"test-app:stop": "kill -9 $(lsof -t -i:8000)",
5052
"test:unit:webbapi:playwright": "mocha test/helper/Playwright_test.js",

Diff for: test/helper/AppiumV2_ios_test.js

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
const assert = require('assert');
2+
const path = require('path');
3+
4+
const Appium = require('../../lib/helper/Appium');
5+
const AssertionFailedError = require('../../lib/assert/error');
6+
const fileExists = require('../../lib/utils').fileExists;
7+
global.codeceptjs = require('../../lib');
8+
9+
let app;
10+
// iOS test app is built from https://github.com/appium/ios-test-app and uploaded to Saucelabs
11+
const apk_path = 'storage:filename=TestApp-iphonesimulator.zip';
12+
const smallWait = 3;
13+
14+
describe('Appium iOS Tests', function () {
15+
this.timeout(0);
16+
17+
before(async () => {
18+
global.codecept_dir = path.join(__dirname, '/../data');
19+
app = new Appium({
20+
app: apk_path,
21+
appiumV2: true,
22+
desiredCapabilities: {
23+
'sauce:options': {
24+
appiumVersion: '2.0.0',
25+
},
26+
browserName: '',
27+
recordVideo: 'false',
28+
recordScreenshots: 'false',
29+
platformName: 'iOS',
30+
platformVersion: '12.2',
31+
deviceName: 'iPhone 8 Simulator',
32+
androidInstallTimeout: 90000,
33+
appWaitDuration: 300000,
34+
},
35+
restart: true,
36+
protocol: 'http',
37+
host: 'ondemand.saucelabs.com',
38+
port: 80,
39+
user: process.env.SAUCE_USERNAME,
40+
key: process.env.SAUCE_ACCESS_KEY,
41+
});
42+
await app._beforeSuite();
43+
app.isWeb = false;
44+
await app._before();
45+
});
46+
47+
after(async () => {
48+
await app._after();
49+
});
50+
51+
describe('app installation : #removeApp', () => {
52+
describe(
53+
'#grabAllContexts, #grabContext, #grabOrientation, #grabSettings',
54+
() => {
55+
it('should grab all available contexts for screen', async () => {
56+
await app.resetApp();
57+
const val = await app.grabAllContexts();
58+
assert.deepEqual(val, ['NATIVE_APP']);
59+
});
60+
61+
it('should grab current context', async () => {
62+
const val = await app.grabContext();
63+
assert.equal(val, 'NATIVE_APP');
64+
});
65+
66+
it('should grab custom settings', async () => {
67+
const val = await app.grabSettings();
68+
assert.deepEqual(val, { imageElementTapStrategy: 'w3cActions' });
69+
});
70+
},
71+
);
72+
});
73+
74+
describe('device orientation : #seeOrientationIs #setOrientation', () => {
75+
it('should return correct status about device orientation', async () => {
76+
await app.seeOrientationIs('PORTRAIT');
77+
try {
78+
await app.seeOrientationIs('LANDSCAPE');
79+
} catch (e) {
80+
e.should.be.instanceOf(AssertionFailedError);
81+
e.inspect().should.include('expected orientation to be LANDSCAPE');
82+
}
83+
});
84+
});
85+
86+
describe('#hideDeviceKeyboard', () => {
87+
it('should hide device Keyboard @quick', async () => {
88+
await app.resetApp();
89+
await app.click('~IntegerA');
90+
try {
91+
await app.click('~locationStatus');
92+
} catch (e) {
93+
e.message.should.include('element');
94+
}
95+
await app.hideDeviceKeyboard('pressKey', 'Done');
96+
await app.click('~locationStatus');
97+
});
98+
99+
it('should assert if no keyboard', async () => {
100+
try {
101+
await app.hideDeviceKeyboard('pressKey', 'Done');
102+
} catch (e) {
103+
e.message.should.include('An unknown server-side error occurred while processing the command. Original error: Soft keyboard not present, cannot hide keyboard');
104+
}
105+
});
106+
});
107+
108+
describe('see text : #see', () => {
109+
it('should work inside elements @second', async () => {
110+
await app.resetApp();
111+
await app.see('Compute Sum', '~ComputeSumButton');
112+
});
113+
});
114+
115+
describe('#appendField', () => {
116+
it('should be able to send special keys to element @second', async () => {
117+
await app.resetApp();
118+
await app.waitForElement('~IntegerA', smallWait);
119+
await app.click('~IntegerA');
120+
await app.appendField('~IntegerA', '1');
121+
await app.hideDeviceKeyboard('pressKey', 'Done');
122+
await app.see('1', '~IntegerA');
123+
});
124+
});
125+
126+
describe('#waitForText', () => {
127+
it('should return error if not present', async () => {
128+
try {
129+
await app.waitForText('Nothing here', 1, '~IntegerA');
130+
} catch (e) {
131+
e.message.should.contain('element (~IntegerA) is not in DOM or there is no element(~IntegerA) with text "Nothing here" after 1 sec');
132+
}
133+
});
134+
});
135+
136+
describe('#seeNumberOfElements @second', () => {
137+
it('should return 1 as count', async () => {
138+
await app.resetApp();
139+
await app.seeNumberOfElements('~IntegerA', 1);
140+
});
141+
});
142+
143+
describe('see element : #seeElement, #dontSeeElement', () => {
144+
it('should check visible elements on page @quick', async () => {
145+
await app.resetApp();
146+
await app.seeElement('~IntegerA');
147+
await app.dontSeeElement('#something-beyond');
148+
await app.dontSeeElement('//input[@id="something-beyond"]');
149+
});
150+
});
151+
152+
describe('#click @quick', () => {
153+
it('should click by accessibility id', async () => {
154+
await app.resetApp();
155+
await app.tap('~ComputeSumButton');
156+
await app.see('0');
157+
});
158+
});
159+
160+
describe('#fillField @second', () => {
161+
it('should fill field by accessibility id', async () => {
162+
await app.resetApp();
163+
await app.waitForElement('~IntegerA', smallWait);
164+
await app.click('~IntegerA');
165+
await app.fillField('~IntegerA', '1');
166+
await app.hideDeviceKeyboard('pressKey', 'Done');
167+
await app.see('1', '~IntegerA');
168+
});
169+
});
170+
171+
describe('#grabTextFrom, #grabValueFrom, #grabAttributeFrom @quick', () => {
172+
it('should grab text from page', async () => {
173+
await app.resetApp();
174+
const val = await app.grabTextFrom('~ComputeSumButton');
175+
assert.equal(val, 'Compute Sum');
176+
});
177+
178+
it('should grab attribute from element', async () => {
179+
await app.resetApp();
180+
const val = await app.grabAttributeFrom('~ComputeSumButton', 'label');
181+
assert.equal(val, 'Compute Sum');
182+
});
183+
184+
it('should be able to grab elements', async () => {
185+
await app.resetApp();
186+
const id = await app.grabNumberOfVisibleElements('~ComputeSumButton');
187+
assert.strictEqual(1, id);
188+
});
189+
});
190+
191+
describe('#saveScreenshot', () => {
192+
beforeEach(() => {
193+
global.output_dir = path.join(global.codecept_dir, 'output');
194+
});
195+
196+
it('should create a screenshot file in output dir', async () => {
197+
const sec = (new Date()).getUTCMilliseconds();
198+
await app.saveScreenshot(`screenshot_${sec}.png`);
199+
assert.ok(fileExists(path.join(global.output_dir, `screenshot_${sec}.png`)), null, 'file does not exists');
200+
});
201+
});
202+
});

0 commit comments

Comments
 (0)