text: module.exports = function () {
    /**
     * Create a single bucket that will be shared by all s3 object scenarios.
     */
    this.Before("@s3", "@objects", function (callback) {
  
      // execute only once
      if (this.sharedBucket) {
        callback();
        return;
      }
  
      this.sharedBucket = 'aws-sdk-js-integration-' +
        this.AWS.util.date.unixTimestamp() * 1000;
  
      this.s3.createBucket({Bucket:this.sharedBucket}, function(err, data) {
        callback();
      });
    });
  
    this.When(/^I write (buffer )?"([^"]*)" to the key "([^"]*)"$/, function(buffer, contents, key, next) {
      var params = {Bucket: this.sharedBucket, Key: key, Body: buffer ? new Buffer(contents) : contents};
      this.request('s3', 'putObject', params, next);
    });
  
    this.When(/^I write "([^"]*)" to the key "([^"]*)" with ContentLength (\d+)$/, function(contents, key, contentLength, next) {
      var params = {Bucket: this.sharedBucket, Key: key, Body: contents, ContentLength: parseInt(contentLength)};
      this.s3nochecksums = new this.AWS.S3({computeChecksums: false});
      this.request('s3nochecksums', 'putObject', params, next);
    });
  
    this.Then(/^the object with the key "([^"]*)" should contain "([^"]*)"$/, function(key, contents, next) {
      this.assert.equal(this.data.Body.toString().replace("\n", ""), contents);
      next();
    });
  
    this.When(/^I copy an object with the key "([^"]*)" to "([^"]*)"$/, function(key1, key2, next) {
      var params = {
        Bucket: this.sharedBucket, Key: key2, CopySource: '/' + this.sharedBucket + '/' + key1
      }
      this.request('s3', 'copyObject', params, next);
    });
  
    this.When(/^I delete the object with the key "([^"]*)"$/, function(key, next) {
      var params = {Bucket:this.sharedBucket,Key:key};
      this.request('s3', 'deleteObject', params, next);
    });
  
    this.Then(/^the object with the key "([^"]*)" should (not )?exist$/, function(key, shouldNotExist, next) {
      var params = { Bucket:this.sharedBucket, Key:key };
      this.eventually(next, function (retry) {
        retry.condition = function() {
          if (shouldNotExist) {
            return this.error && this.error.code == 'NoSuchKey';
          } else {
            return !this.error;
          }
        };
        this.request('s3', 'getObject', params, retry, false);
      });
    });
  
    this.When(/^I write file "([^"]*)" to the key "([^"]*)"$/, function(filename, key, next) {
      var fs = require('fs');
      var params = {Bucket: this.sharedBucket, Key: key, Body:
        fs.createReadStream(__dirname + '/../../extra/fixtures/' + filename)};
      this.request('s3', 'putObject', params, next);
    });
  
    this.When(/^I stream key "([^"]*)"$/, function(key, callback) {
      var params = {Bucket: this.sharedBucket, Key: key};
      var world = this;
      this.result = '';
      this.service.getObject(params).createReadStream().
        on('end', function() { callback(); }).
        on('data', function(d) { world.result += d.toString(); });
    });
  
    this.When(/^I stream2 key "([^"]*)"$/, function(key, callback) {
      if (!require('stream').Readable) return callback();
      var params = {Bucket: this.sharedBucket, Key: key};
      var world = this;
      this.result = '';
      var stream = this.service.getObject(params).createReadStream();
      stream.on('end', function() { callback(); });
      stream.on('readable', function() {
        var v = stream.read(); if (v) world.result += v;
      });
    });
  
    this.Then(/^the streamed data should contain "([^"]*)"$/, function(data, callback) {
      this.assert.equal(this.result.replace('\n', ''), data);
      callback();
    });
  
    this.When(/^I get a pre\-signed URL to GET the key "([^"]*)"$/, function(key, callback) {
      this.signedUrl = this.s3.getSignedUrl('getObject', {Bucket: this.sharedBucket, Key: key});
      callback();
    });
  
    this.When(/^I access the URL via HTTP GET$/, function(callback, verb) {
      var world = this;
      this.data = '';
      require('https').get(this.signedUrl, function (res) {
        res.on('data', function (chunk) {
          world.data += chunk.toString();
        }).on('end', callback);
      }).on('error', callback.fail);
    });
  
    this.Given(/^I get a pre\-signed URL to PUT the key "([^"]*)"(?: with data "([^"]*)")?$/, function(key, body, callback) {
      var params = {Bucket: this.sharedBucket, Key: key};
      if (body) params.Body = body;
      this.signedUrl = this.s3.getSignedUrl('putObject', params);
      callback();
    });
  
    this.Given(/^I access the URL via HTTP PUT with data "([^"]*)"$/, function(body, callback) {
      var world = this;
      this.data = '';
  
      var data = body;
      var options = require('url').parse(this.signedUrl);
      options.method = 'PUT';
      options.headers = {'Content-Length': data.length};
  
      var req = require('https').request(options, function (res) {
        res.on('data', function (chunk) {
          world.data += chunk.toString();
        }).on('end', callback);
      }).on('error', callback.fail).end(data);
    });
  
    this.Then(/^the HTTP response should equal "([^"]*)"$/, function(data, callback) {
      this.assert.equal(this.data, data);
      callback();
    });
  
    this.Then(/^the HTTP response should contain "([^"]*)"$/, function(data, callback) {
      this.assert.match(this.data, data);
      callback();
    });
  
    this.Given(/^I setup the listObjects request for the bucket$/, function(callback) {
      this.params = { Bucket: this.sharedBucket };
      callback();
    });
  
    // progress events
  
    this.When(/^I write a (\d+)KB buffer to the key "([^"]*)" with progress events$/, function(kb, key, callback) {
      var self = this;
      var body = new Buffer(new Array(kb * 1024 + 1).join('x'));
      this.progress = [];
      var req = this.s3.putObject({Bucket: this.sharedBucket, Key: key, Body: body});
      req.on('httpUploadProgress', function (p) { self.progress.push(p) });
      req.send(callback);
    });
  
    this.Then(/^more than (\d+) "([^"]*)" event should fire$/, function(numEvents, eventName, callback) {
      this.assert.compare(this.progress.length, '>', numEvents);
      callback();
    });
  
    this.Then(/^the "([^"]*)" value of the progress event should equal (\d+)KB$/, function(prop, kb, callback) {
      this.assert.equal(this.progress[0][prop], kb * 1024);
      callback();
    });
  
    this.Then(/^the "([^"]*)" value of the first progress event should be greater than (\d+) bytes$/, function(prop, bytes, callback) {
      this.assert.compare(this.progress[0][prop], '>', bytes);
      callback();
    });
  
    this.When(/^I read the key "([^"]*)" with progress events$/, function(key, callback) {
      var self = this;
      this.progress = [];
      var req = this.s3.getObject({Bucket: this.sharedBucket, Key: key});
      req.on('httpDownloadProgress', function (p) { self.progress.push(p) });
      req.send(callback);
    });
  
    // this scenario is a work around for not having an after all hook
    this.Then(/^I delete the shared bucket$/, function(next) {
      this.request('s3', 'deleteBucket', {Bucket:this.sharedBucket}, next);
    });
  };
  